Compare commits

...

1041 commits

Author SHA1 Message Date
Protesilaos 722e27069c
herbstluftwm: make the active border colour less intense 2026-09-04 16:01:56 +03:00
Protesilaos cd18b9f67a
emacs: make prot-emacs-completion-in-buffer also support corfu + completion-preview-mode option 2026-09-04 15:57:37 +03:00
Protesilaos 59d9078de9
emacs: make prot-emacs-completion-in-buffer also support corfu + completion-preview-mode option 2026-09-04 15:56:57 +03:00
Protesilaos 77bb9978c2
emacs: tweak vertico to use new reverse option without reversing the candidates
This places the prompt at the bottom while keeping the candidates at
the top, like how it is by default with the Completions buffer. That
makes more sense to me when viewing the results of consult-line, for
example.
2026-09-04 14:55:46 +03:00
Protesilaos 89853cc302
emacs: fix typo 2026-09-04 14:55:38 +03:00
Protesilaos f88e54b4f5
emacs: tweak the cape heading 2026-09-04 14:26:51 +03:00
Protesilaos a662dbced5
emacs: include cape in completion-category-overrides 2026-09-04 14:26:30 +03:00
Protesilaos cdf2c7c25d
herbstluft: distinguish between some inner and outer theme styles 2026-09-04 13:53:01 +03:00
Protesilaos 6e253caf22
emacs: reorder settings in the completion-preview section 2026-09-04 12:22:43 +03:00
Protesilaos 4ac1f352f4
herbstluft: use different colour for active framee background
This is because we might have another, say, white terimnal so it can
get confusing to make the focusd empty frame look like it also has a
terminal in it.
2026-09-04 09:20:31 +03:00
Protesilaos 73b1b4afad
herbstluft: simplify the colour configuration 2026-09-04 09:11:38 +03:00
Protesilaos 4921e1c7f1
emacs: remove back delete commands from completion-preview-commands 2026-09-03 21:44:26 +03:00
Protesilaos c31c524135
emacs: add note about de facto obsolete mct 2026-09-03 21:42:09 +03:00
Protesilaos d52d443e08
herbstluft: add colour for selected frame that has no windows in it 2026-09-02 15:23:04 +03:00
Protesilaos 03d39e0b66
herbstluft: use grey colours for the active border
Those work better with practically all applications and their colour schemes.
2026-09-02 10:02:43 +03:00
Protesilaos 05b70f3438
emacs: disable electric-quote-mode
I thought it was inserting quote pairs, but what it actually does is
try to be smart about how fancy the quotes are, which I do not like.
If I type `example' in an Elisp comment, this is exactly what I want
to see.
2026-09-01 06:02:32 +03:00
Protesilaos fcd246c3d4
emacs: update examples of prot-emacs-install-vc and also fix mandoura URL 2026-08-31 14:56:03 +03:00
Protesilaos d4aba6b5ff
emacs: add require for package.el and tweak prot-emacs-install 2026-08-31 14:55:34 +03:00
Protesilaos 0dd7c0b40b
emacs: update the comments about completion-preview-mode 2026-08-30 21:43:02 +03:00
Protesilaos 9d269ae5ae
emacs: include org-self-insert-command to completion-preview-commands 2026-08-30 15:00:49 +03:00
Protesilaos 43d549d985
emacs: configure cape to be active unless company is enabled 2026-08-30 15:00:20 +03:00
Protesilaos 971029cda0
emacs: enable electric functionality almost everywhere 2026-08-29 19:24:32 +03:00
Protesilaos 7d0fa5668e
emacs: change prot-icons-style to use emoji for a little while 2026-08-29 19:14:21 +03:00
Protesilaos 1aad9f27da
emacs: make a stylistic change in prot-icons.el 2026-08-29 19:13:57 +03:00
Protesilaos d9026d2d27
emacs: update some of the prot-icons-emoji 2026-08-29 19:13:34 +03:00
Protesilaos 5606dcc95f
emacs: add and reorganise some Emacs 31 settings for VC 2026-08-29 19:12:50 +03:00
Protesilaos 6d8a394f37
emacs: tweak the completion-preview-mode configuration 2026-08-29 19:12:18 +03:00
Protesilaos 904a770d31
emacs: make prot-emacs-abbrev set the ; prefix for the given table 2026-08-29 19:07:14 +03:00
Protesilaos 536b15c5f6
emacs: make prot-common-truncate-lines-silently override 'message' 2026-08-28 11:13:14 +03:00
Protesilaos 6378548705
herbstluftwm: tweak the colours to not rely on transparency
I want to remove the picom dependency, because it may be causing
performance issues.
2026-08-28 10:26:48 +03:00
Protesilaos 421f8fa5a1
herbstluftwm: configure show_frame_decorations 2026-08-28 10:07:48 +03:00
Protesilaos 2840a43cd1
emacs: tweak the company configuration 2026-08-28 06:27:16 +03:00
Protesilaos 08a4ca48b3
emacs: change query-replace-show-preview to a custom function 2026-08-25 10:33:24 +03:00
Protesilaos 6126f1ba44
emacs: also match vc-dir regexp in display-buffer-alist
The derived-mode does not seem to work all the time.
2026-08-24 12:26:58 +03:00
Protesilaos 3ac5eafdf7
emacs: tweak the xref and grep settings for using ripgrep 2026-08-24 12:26:48 +03:00
Protesilaos bf391c6eb1
emacs: implement prot-search-grep-to-dired 2026-08-24 12:26:48 +03:00
Protesilaos 49a825ca3e
emacs: move vc handling of prot-emacs-install into prot-emacs-install-vc
This is easier to reason about.
2026-08-24 12:26:48 +03:00
Protesilaos ccfe49b043
mbsync: clarify commenet about MaildirStore Path and Inbox settings 2026-08-24 12:14:41 +03:00
Protesilaos 1c4cce305e
emacs: make minor refinements to display-buffer-alist 2026-08-15 21:41:13 +03:00
Protesilaos d332c08c28
emacs: add section related to query-replace 2026-08-15 21:40:17 +03:00
Protesilaos 9069f5e88b
emacs: make refinements for flymake 2026-08-15 21:39:58 +03:00
Protesilaos 0f3cdd9bf7
emacs: make outline-xref always use xref-show-definitions-completing-read 2026-08-15 21:39:21 +03:00
Protesilaos aeb94bc513
emacs: remove some of the abbrev definitions that I do not use anymore 2026-08-15 21:38:06 +03:00
Protesilaos 007fa0c1ec
emacs: connect cursory with prot-streaming-mode 2026-08-15 21:37:35 +03:00
Protesilaos 7dd11916e8
emacs: expand some comments in the Org document 2026-08-15 21:37:10 +03:00
Protesilaos 0c4d928bc4
emacs: make minor refinements to prot-minibuffer 2026-08-15 21:36:35 +03:00
Protesilaos 6f85a2eb17
emacs: extend prot-minibuffer to cover input methods 2026-08-15 21:36:22 +03:00
Protesilaos ceb013bf88
emacs: tweak display-buffer-alist to include vc-compilation-mode 2026-08-12 06:51:39 +03:00
Protesilaos dee0a8eeb9
emacs: add show-paren-not-in-comments-or-strings for reference 2026-08-12 06:43:17 +03:00
Protesilaos ef03df0e76
emacs: change the placement of an Org variable 2026-08-12 06:42:30 +03:00
Protesilaos 5e1c88ec50
emacs: include abbrev backend for completion-at-point-functions 2026-08-12 06:41:04 +03:00
Protesilaos 48dd11f1d3
emacs: add binding for unexpand-abbrev 2026-08-12 06:40:40 +03:00
Protesilaos d6e9727316
emacs: include world-clock-sort-order for reference 2026-08-12 06:40:18 +03:00
Protesilaos a0702b24d5
emacs: use correct definition for some nil keybindings 2026-08-12 06:39:50 +03:00
Protesilaos 3f2909a969
emacs: add speedbar note about patch I just sent 2026-08-05 17:08:59 +03:00
Protesilaos b6fca99207
emacs: update speedbar variables to hide directories and files 2026-08-05 16:46:36 +03:00
Protesilaos d33751d21a
emacs: add speedbar configuration 2026-08-05 16:14:32 +03:00
Protesilaos dd2fa24e51
emacs: bind minibuffer-{next,previous}-line-completion in the minibuffer-local-completion-map
This means that the completions will be displayed as soon as I type
those keys.
2026-08-05 09:41:19 +03:00
Protesilaos ef98eaa3d4
emacs: clarify the prot-simple-copy-current-path docstring about buffer-file-name 2026-08-02 08:34:21 +03:00
Protesilaos 674a110740
emacs: include the project-any-command in the project-switch-commands
I am not using this directly, but it is good to have here so that I
can share it with others.
2026-08-02 08:33:40 +03:00
Protesilaos 3cadbe03c8
emacs: simplify prot-simple-keyboard-quit-dwim
This is how I used to have it. I am reverting to it because it is
easier for me to share it with people. Whereas the newer one can be
confusing.
2026-08-02 08:32:16 +03:00
Protesilaos 4dc4412c6e
emacs: add links for generic minibuffer completions 2026-07-31 13:55:32 +03:00
Protesilaos 3d2dd92060
emacs: add tmr-timer to the completion-category-overrides 2026-07-31 13:47:24 +03:00
Protesilaos 143af9d42f
emacs: use sorting for prot-minibuffer-pass completion category 2026-07-29 23:29:16 +03:00
Protesilaos bc7ad395f6
emacs: make small tweaks to default completion keybindings 2026-07-29 15:51:04 +03:00
Protesilaos 4b5d83b693
emacs: tweak the completion-category-overrides 2026-07-29 15:51:04 +03:00
Protesilaos 6d4b0527a5
emacs: make prot-minibuffer-completions-describe-at-point also work with minibuffer-visible-completions 2026-07-29 15:51:04 +03:00
Protesilaos 3e9833373f
emacs: support minibuffer-visible-completions in prot-minibuffer-completions-mode 2026-07-28 14:34:42 +03:00
Protesilaos a89769f0cd
emacs: tweak prot-minibuffer-buffer-sort and comment out the prot-minibuffer-buffer-group 2026-07-28 14:33:52 +03:00
Protesilaos 1346cce3a7
emacs: define advice for password-store--completing-read and update how those are installed 2026-07-28 14:33:12 +03:00
Protesilaos d22a35fa40
emacs: be more specific of where goto-address-mode is enabled in text-mode 2026-07-28 14:31:42 +03:00
Protesilaos a43d806a74
emacs: remove needless prot-common from prot-minibuffer 2026-07-28 14:30:53 +03:00
Protesilaos a18327ff07
emacs: disable the group-function from the buffer category 2026-07-28 14:29:50 +03:00
Protesilaos f81b2884d2
emacs: change the backward-delete-char-untabify-method 2026-07-23 07:49:53 +03:00
Protesilaos 2340545780
emacs: tweak the larger fontaine sizes 2026-07-22 10:01:50 +03:00
Protesilaos 606d3104b9
emacs: mention two of the last videos I did 2026-07-19 15:57:56 +03:00
Protesilaos 68427506c9
emacs: add missing key for completion-preview-mode 2026-07-19 15:57:47 +03:00
Protesilaos 2ae621365c
emacs: refine the company configuration 2026-07-18 21:48:17 +03:00
Protesilaos 3fee36dab6
emacs: try tab-first-completion as nil 2026-07-18 21:48:03 +03:00
Protesilaos fa91c8933d
emacs: change the default value of the lin-face 2026-07-17 14:21:15 +03:00
Protesilaos d539d16e41
emacs: make the gnome-accent-theme-switcher only load in GNOME 2026-07-17 14:20:51 +03:00
Protesilaos 36844d4ba6
emacs: define prot-common--rotate 2026-07-15 16:33:27 +03:00
Protesilaos 109919ced3
emacs: define prot-streaming-mode and related 2026-07-14 18:26:53 +03:00
Protesilaos e14bfda473
emacs: refine Magit display-buffer-alist with prot-window-display-new 2026-07-14 15:11:15 +03:00
Protesilaos 6d6dc50588
emacs: refine the Embark entry for display-buffer-alist 2026-07-14 14:23:18 +03:00
Protesilaos 01913d7354
emacs: update the company configuration to cover changes coming in its next version 2026-07-12 17:45:35 +03:00
Protesilaos 504bf1ea43
emacs: make small tweak to cape configuration 2026-07-12 17:45:14 +03:00
Protesilaos 9826c77f43
emacs: tweak the configuration of cape 2026-07-10 11:37:01 +03:00
Protesilaos b959a72a52
emacs: simplify the vertico keybindings for next/previous group 2026-07-08 14:39:41 +03:00
Protesilaos 27e520da4e
emacs: fix typo 2026-07-08 14:39:11 +03:00
Protesilaos 796bc6154c
emacs: add Mexico City and Phnom Penh to the world clock 2026-07-08 14:38:38 +03:00
Protesilaos 4c0232374b
emacs: disable vc-dir-show-key-binding-hints (Emacs 32) 2026-07-02 12:58:35 +03:00
Protesilaos e7f01f70e3
emacs: add prot-spell-french-punctuation command 2026-07-02 12:58:35 +03:00
Protesilaos c0cb7dba22
emacs: disable prot-register.el
I plan to do something else instead that involves the bookmark system.
2026-07-02 12:58:34 +03:00
Protesilaos 8e24cc6c14
emacs: define prot-dired-count-marks command 2026-07-02 12:58:34 +03:00
Protesilaos 2bbafcbe0f
emacs: include jinx configuration 2026-07-02 12:58:34 +03:00
Protesilaos 805ab6c121
emacs: make the modeline narrow indicator check only major mode, not derivatives 2026-06-21 14:59:17 +03:00
Protesilaos 5112e1a074
emacs: remove obsolete display-time-world-list 2026-06-21 14:57:38 +03:00
Protesilaos 9e0e54711c
emacs: fix error with cape setup 2026-06-17 17:37:14 +03:00
Protesilaos c464b61796
emacs: add option for in-buffer completion and expand the relevant section 2026-06-17 17:22:40 +03:00
Protesilaos 7ec1eea77e
emacs: implement command to copy the current message.el body 2026-06-17 17:22:10 +03:00
Protesilaos 501bf58842
emacs: define prot-dired-search-flat-list-since-days command 2026-06-13 20:04:00 +03:00
Protesilaos ea29f7801e
emacs: tweak the org-agenda-time-grid 2026-06-09 11:24:17 +03:00
Protesilaos f301fefb0e
emacs: make prot-modeline-input-method target the active modeline 2026-06-08 16:14:26 +03:00
Protesilaos d74027c0df
emacs: remove binding of dired-find-file from f 2026-06-06 17:51:12 +03:00
Protesilaos f0ed0454bb
emacs: change the wording of a Dired heading 2026-06-06 17:50:49 +03:00
Protesilaos 8a3066ed3d
emacs: change the keybindings for going to isearch first/last 2026-06-06 17:50:27 +03:00
Protesilaos ba75ec9625
emacs: remove date sections from the Elfeed search buffer 2026-06-01 16:48:19 +03:00
Protesilaos 2e20636f6a
emacs: make elfeed-search-yank mark the entry as read 2026-05-29 16:14:02 +03:00
Protesilaos 230d72c73f
emacs: make elfeed header-line show absolute time of last sync 2026-05-29 10:08:46 +03:00
Protesilaos 48c0b8b15b
emacs: change how elfeed-search-filter is set 2026-05-28 08:54:20 +03:00
Protesilaos a214127f22
emacs: revise prot-search to remove keys/commands I do not need 2026-05-27 10:17:25 +03:00
Protesilaos 20dd62605e
emacs: update the list of prot-emacs-my-packages 2026-05-27 10:17:10 +03:00
Protesilaos 726be3d6ed
emacs: define prot-dired-open-directory-externally 2026-05-21 10:01:11 +03:00
Protesilaos a31308fdf6
emacs: add prot-window-display-other-or-current and use it for Magit in display-buffer-alist 2026-05-21 10:01:11 +03:00
Protesilaos 0c67e27e4d
emacs: use revert-buffer in elfeed instead of private function 2026-05-18 22:22:06 +03:00
Protesilaos fe2c236afb
emacs: tweak the diff-hl settings 2026-05-18 22:21:55 +03:00
Protesilaos 6d5fea8b7f
emacs: tangle all the files 2026-05-06 09:41:22 +03:00
Protesilaos 3c1dc8bb6e
emacs: include the buffer-to-pdf 2026-05-06 09:40:48 +03:00
Protesilaos 73211a9fb9
emacs: reword some headings 2026-05-06 09:40:37 +03:00
Protesilaos f60b02dfcd
emacs: add source for the modus-themes-exporter 2026-05-06 09:40:22 +03:00
Protesilaos eae08a6e10
emacs: include my modus-themes-exporter 2026-05-06 09:33:18 +03:00
Protesilaos 200681077f
emacs: move scroll-error-top-bottom in the scrolling section 2026-05-03 20:20:16 +03:00
Protesilaos f72ff38ff0
emacs: define prot-search-collect command 2026-05-01 19:25:36 +03:00
Protesilaos edbcc4a643
emacs: simplify prot-elisp-macroexpand-mode 2026-05-01 19:05:10 +03:00
Protesilaos 548bc34c2a
emacs: make small refinement to the display-buffer-alist 2026-05-01 19:04:30 +03:00
Protesilaos 67500c5352
emacs: refine prot-dired-grep-marked-files 2026-04-24 22:27:10 +03:00
Protesilaos d1c7291c38
emacs: simplify prot-icons-directory 2026-04-24 22:24:00 +03:00
Protesilaos 5a8a41ebff
Ignore Emacs autoload files 2026-04-22 14:07:02 +03:00
Protesilaos 6c68acf83c
Change "Protesilaos Stavrou" to "Protesilaos" 2026-04-21 15:55:09 +03:00
Protesilaos Stavrou 051503b0a9
emacs: revise the prot-icons symbolic entry for dired-mode 2026-04-18 09:08:05 +03:00
Protesilaos Stavrou 7b2ed2f0f4
emacs: expand the world-clock list and add emoji to it 2026-04-18 09:07:47 +03:00
Protesilaos Stavrou 1a850788e4
emacs: refine the prot-modeline buffer name indicator for read-only 2026-04-12 19:58:32 +03:00
Protesilaos Stavrou ff673029ae
emacs: add read-only icon to the prot-icons 2026-04-12 19:58:07 +03:00
Protesilaos Stavrou 2d2d2cf53a
emacs: add a mouse keymap for the modeline frame name indicator 2026-04-12 19:06:03 +03:00
Protesilaos Stavrou 86afcc9d72
emacs: reword the doc string of prot-modeline-vc-map 2026-04-12 19:05:40 +03:00
Protesilaos Stavrou 49c8160642
emacs: remove second entry for org-capture in the display-buffer-alist 2026-04-12 18:22:59 +03:00
Protesilaos Stavrou 0cd445395b
emacs: remove the advice for which-function 2026-04-12 18:22:43 +03:00
Protesilaos Stavrou 9b3b09c72a
emacs: update some symbolic prot-icons 2026-04-12 18:05:28 +03:00
Protesilaos Stavrou 7189014484
emacs: fix some bugs with prot-modeline-which-function-indicator 2026-04-12 18:05:09 +03:00
Protesilaos Stavrou 340734653e
emacs: make indentation change to prot-modeline-frame-name 2026-04-12 18:04:40 +03:00
Protesilaos Stavrou f77dde215e
emacs: tweak the spacious-padding value for the width of the fringe 2026-04-10 21:48:39 +03:00
Protesilaos Stavrou 407203c77d
emacs: refine the symbolic prot-icons 2026-04-10 09:42:30 +03:00
Protesilaos Stavrou 96d51ca6b3
emacs: fix typos about "keymap" 2026-04-09 21:14:37 +03:00
Protesilaos Stavrou 89ad48e801
emacs: add more styles to prot-icons 2026-04-08 16:04:17 +03:00
Protesilaos Stavrou 2cf01a8e54
emacs: add prot-elisp-copy-current-definition command 2026-04-07 20:30:47 +03:00
Protesilaos Stavrou 984e15903e
emacs: disable C-M-w 2026-04-07 20:30:26 +03:00
Protesilaos Stavrou 691262dfe5
emacs: add key binding for gnome-accent-theme-switcher-load-random-from-current-set 2026-04-07 20:28:02 +03:00
Protesilaos Stavrou 08e8efe0de
emacs: make RET follow links in Org 2026-04-07 20:27:43 +03:00
Protesilaos Stavrou 643968e197
emacs: switch off magit-diff-fontify-hunk
It is a good feature to have. Though I realised that it is not helping
me in my workflow. By the time I am ready to commit, the diff is just
there for me to confirm I have not missed something.
2026-04-07 20:26:04 +03:00
Protesilaos Stavrou cb325dcfe4
emacs: make lin-gnome-accent-color-mode always work in GNOME 2026-03-24 10:32:41 +02:00
Protesilaos Stavrou 77044008d6
emacs: refine how buffers are sorted 2026-03-24 10:32:23 +02:00
Protesilaos Stavrou 691f47ae9e
emacs: configure the new magit-diff-fontify-hunk 2026-03-21 12:30:03 +02:00
Protesilaos Stavrou 3dd18f9bb1
emacs: add diff-hl package 2026-03-21 12:29:34 +02:00
Protesilaos Stavrou 27ebe1670b
emacs: do not limit corfu to prot-emacs-completion-ui of vertico
That was a mistake on my part.
2026-03-21 10:21:41 +02:00
Protesilaos Stavrou 189df059a6
emacs: comment out the header-args for the main Org file
They prevent the block with the system-configuration-options from
exporting its results.
2026-03-15 18:51:47 +02:00
Protesilaos Stavrou 559be1a5ff
emacs: add logos bindings that are easier to reach 2026-03-12 12:39:14 +02:00
Protesilaos Stavrou 9c1fe22810
emacs: disable the org-columns key binding 2026-03-12 12:37:36 +02:00
Protesilaos Stavrou cf7c37f81c
emacs: add "voilà" to the abbrevs 2026-03-12 12:32:59 +02:00
Protesilaos Stavrou 72461ab316
emacs: cover new vc-display-failed-async-commands variable 2026-03-08 19:03:51 +02:00
Protesilaos Stavrou 455df21f30
emacs: include missing prot-simple update for prot-simple-copy-current-path 2026-03-08 19:03:26 +02:00
Protesilaos Stavrou 799f475c7c
emacs: update the key binding of prot-simple-copy-current-path 2026-03-08 19:03:02 +02:00
Protesilaos Stavrou 96548fa23e
bashrc: remove a space from a shell alias 2026-03-08 12:29:26 +02:00
Protesilaos Stavrou 92598a96af
emacs: stop folding Org heading after pasting them 2026-03-08 12:29:08 +02:00
Protesilaos Stavrou 8063a7ff6a
emacs: define prot-simple-copy-current-path command 2026-03-08 12:28:50 +02:00
Protesilaos Stavrou c09c71cacf
emacs: update the trusted-content directories 2026-03-08 12:28:03 +02:00
Protesilaos Stavrou 8e5bc15205
emacs: make small tweak to the institution-calendar configuration 2026-02-23 14:43:39 +02:00
Protesilaos Stavrou 1c7aa37d56
emacs: make backspace delete tabs in one go rather than untabify them 2026-02-23 14:43:09 +02:00
Protesilaos Stavrou e1085c530d
emacs: include webp in the prot-common-file-extensions 2026-02-20 09:17:14 +02:00
Protesilaos Stavrou b1af3c87de
emacs: add group function for symbols 2026-02-18 13:45:01 +02:00
Protesilaos Stavrou 05eacab88c
emacs: update the prot-minibuffer-file-sort doc string 2026-02-18 13:36:38 +02:00
Protesilaos Stavrou f105f57b6a
emacs: fix match in prot-minibuffer-symbol-sort 2026-02-18 13:34:15 +02:00
Protesilaos Stavrou 49d0038dbf
emacs: include my institution-calendar in the configuration 2026-02-15 09:26:56 +02:00
Protesilaos Stavrou 5dcff0411d
emacs: update prot-emacs-theme.el 2026-02-13 18:51:36 +02:00
Protesilaos Stavrou 627a8fe266
emacs: update the gnome-accent-theme-switcher with key bindings 2026-02-13 18:51:21 +02:00
Protesilaos Stavrou 295660e79c
emacs: update the lin section with its GNOME integration mode 2026-02-13 18:51:04 +02:00
Protesilaos Stavrou 772ed185fc
emacs: add my new package gnome-accent-theme-switcher 2026-02-13 15:14:14 +02:00
Protesilaos Stavrou d3532eb303
emacs: quote the macroexpanded prot-emacs-install VC-ARGS 2026-02-13 15:13:08 +02:00
Protesilaos Stavrou d361c6f7be
emacs: incorporate the doric-themes-with-colors macro 2026-02-08 12:51:38 +02:00
Protesilaos Stavrou f567716412
emacs: show all outline levels when using Ediff 2026-02-08 12:51:11 +02:00
Protesilaos Stavrou 7de2868d6f
emacs: streamline the org-agenda-prefix-format 2026-02-05 10:40:40 +02:00
Protesilaos Stavrou 655bfd998a
emacs: make small refinements to the prot-minibuffer annotation functions 2026-02-04 10:34:05 +02:00
Protesilaos Stavrou 3fce2dd5d1
emacs: add command to include ICS file in the diary 2026-02-04 10:33:36 +02:00
Protesilaos Stavrou b35b471243
emacs: add vertico bindings for next/previous group 2026-02-01 12:38:09 +02:00
Protesilaos Stavrou 9a73b1a3cb
emacs: do not return empty string in prot-minibuffer-file-group 2026-02-01 12:37:35 +02:00
Protesilaos Stavrou 57fddc174b
emacs: fix typo in docstring 2026-02-01 12:37:16 +02:00
Protesilaos Stavrou b8b314038c
bashrc: use consistent values for VISUAL and EDITOR
I set them to these values elsewhere in the file.
2026-01-28 22:27:05 +02:00
Protesilaos Stavrou e9755f0104
emacs: fix a typo 2026-01-28 22:26:51 +02:00
Protesilaos Stavrou d81db7ca3f
emacs: add commands to convert between Fahrenheit and Celsius 2026-01-27 09:03:50 +02:00
Protesilaos Stavrou 2ea544a0f7
emacs: ensure the flex completion style does not accept spaces
Otherwise it interferes with the orderless completion style.
2026-01-25 09:15:30 +02:00
Protesilaos Stavrou 3b67ebadff
emacs: include xz in the archive file extensions 2026-01-23 10:57:17 +02:00
Protesilaos Stavrou 6533c1e82a
emacs: simplify the prot-minibuffer-file-group 2026-01-21 20:45:26 +02:00
Protesilaos Stavrou 2de225373b
emacs: customise the vertico-group-format 2026-01-21 13:02:10 +02:00
Protesilaos Stavrou 67eee8d7c2
emacs: make prot-icons-get-icon propertize 'face' in addition to 'font-lock-face' 2026-01-21 13:01:45 +02:00
Protesilaos Stavrou 52a84228a5
emacs: fix the return value of prot-minibuffer-buffer-group 2026-01-21 13:01:15 +02:00
Protesilaos Stavrou 0bd21cecba
emacs: tweak the completions-group-format 2026-01-21 12:31:48 +02:00
Protesilaos Stavrou 35347c2646
emacs: add group function for buffers 2026-01-21 12:31:31 +02:00
Protesilaos Stavrou 57c7a6906a
emacs: include diff-restrict-view in the enabled commands 2026-01-21 12:29:22 +02:00
Protesilaos Stavrou 1921a50666
emacs: include message-forward-as-mime 2026-01-19 12:42:43 +02:00
Protesilaos Stavrou c10ea2bdd2
emacs: tweak the prot-icons faces when a Modus theme is loaded 2026-01-19 12:42:08 +02:00
Protesilaos Stavrou 83aaa5aab3
emacs: include prot-icons for frames and git that prot-modeline uses 2026-01-19 12:41:49 +02:00
Protesilaos Stavrou 56f1947a25
emacs: tweak the prot-icons for dired-mode 2026-01-19 12:41:18 +02:00
Protesilaos Stavrou 3b8652a10e
rofi: make the window show only the top candidate 2026-01-19 12:40:30 +02:00
Protesilaos Stavrou f3ccaf2bef
emacs: revise the package-review-policy 2026-01-17 12:50:18 +02:00
Protesilaos Stavrou effcd2e544
emacs: make small rewording for the email submodule 2026-01-17 07:27:37 +02:00
Protesilaos Stavrou 5e69cce6db
emacs: fix the value of package-review-policy 2026-01-17 07:14:13 +02:00
Protesilaos Stavrou d58f691a19
emacs: reword a comment for clarity 2026-01-16 08:23:02 +02:00
Protesilaos Stavrou 9925a833d9
emacs: tweak the prot-modeline indicators for frame and Git branch 2026-01-15 17:48:29 +02:00
Protesilaos Stavrou 3e400f3812
emacs: make stylistic refinements to the prot-modeline 2026-01-15 09:05:55 +02:00
Protesilaos Stavrou 41aaea3ba3
emacs: tweak some prot-icons styles 2026-01-14 09:52:08 +02:00
Protesilaos Stavrou c0a1d4bb26
emacs: add missing bold weight to the prot-modeline indicator faces 2026-01-14 08:53:05 +02:00
Protesilaos Stavrou f550a6dca0
emacs: expand the package-review-policy 2026-01-13 07:26:12 +02:00
Protesilaos Stavrou 0aa51aea71
emacs: always use the same face for prot-icons on the modeline 2026-01-13 07:11:28 +02:00
Protesilaos Stavrou 83d2483cec
emacs: refine the prot-icons for directories 2026-01-13 07:11:18 +02:00
Protesilaos Stavrou 8c12ec0568
emacs: make the prot-icons have a background 2026-01-13 07:03:19 +02:00
Protesilaos Stavrou dcf744d223
emacs: tweak the prot-modeline faces that set a background 2026-01-13 06:47:07 +02:00
Protesilaos Stavrou 24913de72b
emacs: include partial-completion among the default completion styles 2026-01-13 06:35:20 +02:00
Protesilaos Stavrou 389bce85cf
emacs: simplify package-pinned-packages 2026-01-11 14:45:49 +02:00
Protesilaos Stavrou b663eee4e1
emacs: fix package-review-policy 2026-01-11 14:44:41 +02:00
Protesilaos Stavrou f2ab651213
emacs: incorporate the new package review policy 2026-01-11 11:25:57 +02:00
Protesilaos Stavrou d44128c794
emacs: make prot-simple-hex-color-mode affect the display property and undo the effect 2026-01-11 11:08:06 +02:00
Protesilaos Stavrou 51e041fb99
emacs: fix prot-simple-hex-fontify 2026-01-10 10:17:07 +02:00
Protesilaos Stavrou 697ba95fae
emacs: define simple hex colour preview because rainbow-mode does not quite work with hl-line-mode 2026-01-10 10:01:35 +02:00
Protesilaos Stavrou 27fcb38ffc
herbstluft: use the middle split in the starting workspace 2026-01-10 08:22:48 +02:00
Protesilaos Stavrou 106af581d8
emacs: stop modifying the face in prot-minibuffer-completions-mode 2026-01-10 08:22:17 +02:00
Protesilaos Stavrou 4a377fe019
emacs: remove a face from prot-icons 2026-01-10 07:43:23 +02:00
Protesilaos Stavrou 3ae02b32f8
emacs: make the ef-themes set bold and italics 2026-01-10 07:42:51 +02:00
Protesilaos Stavrou bdf5688bc5
emacs: include the oxford-calendar in my packages 2026-01-10 07:42:27 +02:00
Protesilaos Stavrou c90065e04b
sxhkd: note that the x-terminal-emulator is for Debian 2026-01-10 07:30:11 +02:00
Protesilaos Stavrou 0016ead2b8
emacs: use Modus colours for the prot-icons when applicable 2026-01-09 10:25:22 +02:00
Protesilaos Stavrou a5eb73dd6d
emacs: commit the copyright changes to some files that I missed earlier 2026-01-09 10:25:04 +02:00
Protesilaos Stavrou 7be95d29f3
emacs: make the prot-modeline faces use colours from the active modus theme 2026-01-08 18:17:20 +02:00
Protesilaos Stavrou 73191d7516
emacs: change how flymake is set up for Elisp packaging 2026-01-08 18:17:20 +02:00
Protesilaos Stavrou 2eb43ac677
Merge pull request #11 from kayzhang/folk-master
emacs: fix typo and add missing autoloads for prot-project.el
2026-01-08 07:05:22 +02:00
Protesilaos Stavrou 73fa300def
Update all copyright notices to use the correct format and current year 2026-01-07 08:13:21 +02:00
Protesilaos Stavrou 168845fc26
emacs: remove outdated comment and move a hook a couple lines down 2026-01-07 08:13:21 +02:00
Protesilaos Stavrou d7453a9a45
emacs: tweak the wording of a heading and sentence below it 2026-01-07 08:13:21 +02:00
Protesilaos Stavrou 01bb3f4cfc
emacs: rename the completion categories I am adding to make them obvious 2026-01-07 08:13:21 +02:00
Protesilaos Stavrou 37e0ab2689
emacs: change the completion metadata of the kill-ring category 2026-01-07 08:13:21 +02:00
Protesilaos Stavrou e4eff354fc
emacs: expand VC section with more keys and vc-annotate colours 2026-01-07 08:13:21 +02:00
Protesilaos Stavrou d3fad85909
emacs: switch to calling denote-fontify-links-mode per latest changes to denote.git 2026-01-06 12:15:05 +02:00
Protesilaos Stavrou 51bad82655
emacs: revert to plain character indicators for notmuch-indicator 2026-01-06 12:14:38 +02:00
Protesilaos Stavrou 40696463c8
emacs: make prot-icons work with dired-do-redisplay 2026-01-06 12:14:20 +02:00
kizha 9a7bc2f6cb emacs: fix typo and add missing autoloads for prot-project.el
1. Fix typo in `prot-window-popup-tmr` -> should be `prot-window-popup-prot-project-switch`
2. Add missing autoloads `prot-project-switch` for prot-project.el
   * This is used by the `prot-window-popup-prot-project-switch` function
2026-01-04 19:40:04 +08:00
Protesilaos Stavrou c8fa11267f
emacs: refine the completion-styles 2026-01-04 09:43:17 +02:00
Protesilaos Stavrou 25b77affa2
emacs: tweak how prot-icons are prepended to xref file headers 2026-01-02 12:37:41 +02:00
Protesilaos Stavrou 17529881f5
emacs: add new key bindings for VC logs 2026-01-01 12:09:01 +02:00
Protesilaos Stavrou d682ef7eee
notmuch: make post-new recognise read flag from the server 2026-01-01 12:08:33 +02:00
Protesilaos Stavrou 88c47b85a2
emacs: add some more prot-icons 2025-12-25 10:36:53 +02:00
Protesilaos Stavrou 59116892fd
emacs: add more file extensions for programming to prot-common-file-extensions 2025-12-25 08:03:15 +02:00
Protesilaos Stavrou 5a822f873f
emacs: refine the prot-icons alignment for dired and xref 2025-12-25 07:37:16 +02:00
Protesilaos Stavrou 036388c739
emacs: use 'button' for the completions-highlight-face 2025-12-23 18:48:34 +02:00
Protesilaos Stavrou 1037b8ab8e
emacs: write some commentary about prot-icons.el 2025-12-23 17:45:03 +02:00
Protesilaos Stavrou 561e51ad78
emacs: load which-key unconditionally 2025-12-23 17:33:15 +02:00
Protesilaos Stavrou 8096123e35
emacs: tweak prot-icons colours and their mappings as well as the characters that are used 2025-12-23 08:09:25 +02:00
Protesilaos Stavrou 22adf95dea
emacs: use dired face for prot-icons directories 2025-12-22 07:24:59 +02:00
Protesilaos Stavrou 86cd15cdd4
emacs: remove needless annotation for files 2025-12-22 04:59:09 +02:00
Protesilaos Stavrou 08ed2d4bcf
emacs: sort files by group and alphabetically 2025-12-22 04:59:09 +02:00
Protesilaos Stavrou e59d96b71c
emacs: add prot-icons and integrate them where relevant 2025-12-21 09:18:45 +02:00
Protesilaos Stavrou a1a7f9101a
emacs: use a different face for the completions so it stands out more 2025-12-20 11:04:09 +02:00
Protesilaos Stavrou cea7b6e111
emacs: remove needless commentary from files 2025-12-19 18:05:35 +02:00
Protesilaos Stavrou abc737d84c
emacs: go back to including orderless in prot-emacs-completion-extras 2025-12-19 18:05:35 +02:00
Protesilaos Stavrou 4a94b3a81d
emacs: write prot-minibuffer.el and make relevant changes to the configuration 2025-12-19 18:05:35 +02:00
Protesilaos Stavrou 357c29ba34
emacs: fix prot/crm-p 2025-12-18 18:16:42 +02:00
Protesilaos Stavrou 39edb50e2f
emacs: refine how prot-shell is loaded 2025-12-18 10:22:35 +02:00
Protesilaos Stavrou b2c4daf178
emacs: make prot-register-store work only if the file exists 2025-12-18 10:21:47 +02:00
Protesilaos Stavrou 0b4c9edfcd
emacs: make stylistic refinements to prot-register.el 2025-12-17 17:56:40 +02:00
Protesilaos Stavrou 5909fd6dd2
emacs: tweak the register-preview-delay 2025-12-17 17:56:20 +02:00
Protesilaos Stavrou 4ee00e6de2
emacs: define prot-register-persist-mode and tweak how I set up registers 2025-12-17 17:18:58 +02:00
Protesilaos Stavrou 8d5909b77c
emacs: define command prot-simple-delete-window-dwim 2025-12-17 17:18:57 +02:00
Protesilaos Stavrou f6a423159f
emacs: add some comments about corfu and completion-preview-mode 2025-12-17 17:18:57 +02:00
Protesilaos Stavrou 108a81175a
emacs: load corfu only if vertico is loaded 2025-12-17 16:43:26 +02:00
Protesilaos Stavrou b4acab0624
emacs: hide the mode line of the Completions
It does not always resize it well, but I still prefer this style if I
am using the generic user interface.
2025-12-17 16:42:41 +02:00
Protesilaos Stavrou 3753e9e3f5
emacs: ensure prot/crm-p does not error out when crm has not been loaded yet 2025-12-17 16:42:26 +02:00
Protesilaos Stavrou 8f7b2a1b76
emacs: tweak the completion-styles and the override for a few categories 2025-12-17 16:40:47 +02:00
Protesilaos Stavrou 230d2d0795
emacs: tweak how consult-denote is loaded 2025-12-16 07:30:35 +02:00
Protesilaos Stavrou d376ac7d96
emacs: make further refinements to the generic completions 2025-12-16 07:29:35 +02:00
Protesilaos Stavrou befb4e8ff2
emacs: make minibuffer variables also be case-insensitive 2025-12-16 07:28:50 +02:00
Protesilaos Stavrou fd8c4679de
emacs: add sort and group functions for files and symbols 2025-12-16 07:28:26 +02:00
Protesilaos Stavrou 32f26e11d0
emacs: refine how completion styles are by default and in some overrides 2025-12-14 06:55:31 +02:00
Protesilaos Stavrou 96a0fce25c
emacs: use maximalist style for vertico theme completion category 2025-12-14 06:26:22 +02:00
Protesilaos Stavrou ba847a636f
emacs: use the vertico function to sort directories instead of my own 2025-12-14 06:25:51 +02:00
Protesilaos Stavrou 219d54cbf5
emacs: clarify a comment about spacious-padding 2025-12-14 06:25:51 +02:00
Protesilaos Stavrou 457fdf6ee0
emacs: do not print results in the dotemacs org file
This is in case I accidentally type C-c C-v C-b.
2025-12-13 17:40:33 +02:00
Protesilaos Stavrou 715cc8cf2e
emacs: specify the user-lisp-directory (Emacs 31) 2025-12-13 17:35:57 +02:00
Protesilaos Stavrou ec42c4b637
emacs: review the org-capture configuration 2025-12-13 17:20:26 +02:00
Protesilaos Stavrou dad0eff2a1
emacs: add commentary about what makes generic completions not good enough 2025-12-13 17:02:40 +02:00
Protesilaos Stavrou 440618744a
emacs: include reference to minibuffer-visible-completions 2025-12-13 16:59:12 +02:00
Protesilaos Stavrou 3f5c1c2376
emacs: add missing key binding for denote-grep 2025-12-13 05:23:25 +02:00
Protesilaos Stavrou 93c2efffca
emacs: load elfeed-feeds once 2025-12-13 05:02:48 +02:00
Protesilaos Stavrou 39975bea83
emacs: include theme in completion-category-overrides 2025-12-13 05:01:52 +02:00
Protesilaos Stavrou c065ef8013
emacs: disambiguate emoji completion category from unicode name 2025-12-13 05:01:16 +02:00
Protesilaos Stavrou 47fb6d8b72
emacs: add back the partial-completion style that I removed by mistake 2025-12-13 05:00:52 +02:00
Protesilaos Stavrou ac547b252f
emacs: use correct symbol for completion style overrides for unicode-name category 2025-12-12 09:32:34 +02:00
Protesilaos Stavrou b2aefa8931
emacs: refine prot-simple-keyboard-quit-dwim to not delete single window 2025-12-12 09:21:49 +02:00
Protesilaos Stavrou ec481ce0af
emacs: use second-tab value for completion-auto-select everywhere 2025-12-12 09:21:17 +02:00
Protesilaos Stavrou 11ba02773e
emacs: tweak the completion style overrides for a few categories 2025-12-12 09:20:52 +02:00
Protesilaos Stavrou d284cc3ec0
emacs: simplify the default value of the completion-styles 2025-12-12 09:17:43 +02:00
Protesilaos Stavrou 7139025471
emacs: add missing completion category to emoji--read-emoji 2025-12-12 09:16:54 +02:00
Protesilaos Stavrou 294c56c979
emacs: load orderless unconditionally 2025-12-12 09:01:10 +02:00
Protesilaos Stavrou 9b3e48d6af
emacs: make further refinements to the completion-styles and completion-category-overrides 2025-12-11 08:19:40 +02:00
Protesilaos Stavrou 1d01db4d16
emacs: expand the customisation of the generic minibuffer completions 2025-12-11 07:36:51 +02:00
Protesilaos Stavrou 3b7e2373be
emacs: update the completion-category-overrides 2025-12-11 07:36:27 +02:00
Protesilaos Stavrou c33581bd8f
emacs: refine how orderless is appended to the completion-styles 2025-12-11 07:36:01 +02:00
Protesilaos Stavrou 8a84e58d93
emacs: stop setting completion-pcm-leading-wildcard 2025-12-11 07:36:01 +02:00
Protesilaos Stavrou 9089ee5355
emacs: configure help-window-keep-selected 2025-12-11 07:36:01 +02:00
Protesilaos Stavrou bb6a4526cb
emacs: make prot-simple-keyboard-quit-dwim do more of what I mean 2025-12-11 07:36:01 +02:00
Protesilaos Stavrou 4fbd7ad9b7
emacs: remove old obsolete function from prot-simple.el 2025-12-11 07:36:01 +02:00
Protesilaos Stavrou 7d4b666302
emacs: stop using completion-preview 2025-12-11 03:33:08 +02:00
Protesilaos Stavrou fb68065d89
emacs: add an extra condition to prot-simple-keyboard-quit-dwim 2025-12-10 09:38:31 +02:00
Protesilaos Stavrou 77285c8eec
emacs: make prot-org--capture-coach-person-prompt work even with generic minibuffer UI 2025-12-06 09:18:28 +02:00
Protesilaos Stavrou 5a03195153
emacs: simplify how nerd-icons-completion is loaded 2025-12-02 12:34:47 +02:00
Protesilaos Stavrou 8a425233e3
emacs: refine how orderless is loaded 2025-12-02 09:49:11 +02:00
Protesilaos Stavrou cc5032df7a
emacs: do the tangling I missed in the previous commits 2025-12-02 06:37:16 +02:00
Protesilaos Stavrou de6b7c42cf
emacs: include the completion-preview-mode 2025-12-02 06:36:53 +02:00
Protesilaos Stavrou dcc1e0723a
emacs: redo the settings I have for generic minibuffer completions 2025-12-02 06:36:37 +02:00
Protesilaos Stavrou 2128328c70
emacs: simplify the completion-category-overrides 2025-12-02 06:36:12 +02:00
Protesilaos Stavrou 7f92e14a32
emacs: broaden the scope of prot-emacs-completion-extras 2025-12-02 06:34:31 +02:00
Protesilaos Stavrou c32043cf34
emacs: include my projects in elisp-flymake-byte-compile-load-path 2025-12-01 17:44:58 +02:00
Protesilaos Stavrou fcd66b48cb
emacs: clarify text about experiments with pgtk 2025-12-01 17:44:38 +02:00
Protesilaos Stavrou c89ec43259
emacs: tangle the missing files 2025-11-28 07:26:22 +02:00
Protesilaos Stavrou 5e02e72023
emacs: comment on the experiment with pgtk 2025-11-28 07:25:57 +02:00
Protesilaos Stavrou 4e810c7e95
emacs: explicitly disable the various toolkit bars
Setting them to 0 actually does not disable them. So if you do M-x
menu-bar-mode you have to do it twice to enable it.
2025-11-28 07:25:00 +02:00
Protesilaos Stavrou 11059eb5ec
emacs: set the scroll bar width subject to pgtk 2025-11-28 07:24:32 +02:00
Protesilaos Stavrou 12843be87c
emacs: add frame parameters subject to PGTK build
I need to experiment with it for a while.
2025-11-28 07:18:14 +02:00
Protesilaos Stavrou 709997007d
emacs: integrate beframe+consult as a buffer source
This way I can get both the frame-local and global buffer list.
2025-11-27 07:01:37 +02:00
Protesilaos Stavrou 2e1b781723
emacs: undecorate frames by default 2025-11-23 12:45:19 +02:00
Protesilaos Stavrou 4db167e261
emacs: include denote-merge in my prot-emacs-my-packages 2025-11-21 07:46:21 +02:00
Protesilaos Stavrou 81858f8329
emacs: fix a typo in prot-simple that broke a function 2025-11-21 07:46:05 +02:00
Protesilaos Stavrou 16d22ac49f
emacs: use a different character for the org-ellipsis and the magit equivalent 2025-11-17 10:01:57 +02:00
Protesilaos Stavrou f38d808707
emacs: make stylistic change to prot-simple-unfill-region-or-paragraph 2025-11-16 07:37:01 +02:00
Protesilaos Stavrou a8dea72bf9
emacs: use the which-func face in the prot-modeline-which-function-indicator 2025-11-16 07:36:38 +02:00
Protesilaos Stavrou 647c85c8bc
emacs: set the eww-open-file in dired-mode-map when appropriate 2025-11-16 07:36:13 +02:00
Protesilaos Stavrou feeef6d99c
emacs: update the keybindings for the doric-themes configuration 2025-11-16 07:13:09 +02:00
Protesilaos Stavrou c2978f2f7c
emacs: update the pulsar configuration 2025-11-11 07:30:04 +02:00
Protesilaos Stavrou 266928d65b
emacs: update C-x L keybinding to in-development pulsar command 2025-11-10 16:57:36 +02:00
Protesilaos Stavrou 91283c648a
emacs: simplify the definition of magit-repolist-columns 2025-11-09 19:39:49 +02:00
Protesilaos Stavrou 1bc651b600
emacs: stop using the fringe indicators in Magit
I tried them for a while. They are okay, but I prefer to have that at
the end of the line as text that is consistent with my font rather
than some bitmap.
2025-11-09 19:39:49 +02:00
Protesilaos Stavrou 338054d1e4
emacs: note that flymake-show-diagnostics-at-end-of-line has a new value
I still do not want to use it, but it is an interesting feature.
2025-11-09 19:35:38 +02:00
Protesilaos Stavrou 2ca13eba98
emacs: use lexical binding everywhere by default 2025-11-09 19:34:33 +02:00
Protesilaos Stavrou d1fc98559a
emacs: enable org-indent-mode by default and specify org-startup-folded 2025-11-07 05:47:33 +02:00
Protesilaos Stavrou eb709a302d
emacs: remove the extra vc parts from prot-modeline
The diffstat was an interesting idea that I ultimately had no use for.
Showing the name of the project also is of marginal value, as that
information is avaible in various places, include Magit buffers, the
Beframe frame title, and more.
2025-11-06 17:44:47 +02:00
Protesilaos Stavrou aed4782a43
emacs: fix some loading issues 2025-11-06 10:30:01 +02:00
Protesilaos Stavrou 6da74c79a3
emacs: ensure vc-git-stash exists when trying to use it in prot-simple-update-package-repositories-subr
Also, the message has to be more vague because there may be other
errors instead of just not being able to pull (e.g. there is no
tracking information about the remote branch).
2025-11-06 08:34:47 +02:00
Protesilaos Stavrou d8a0d4f811
emacs: show directory name in VC mode line status when relevant 2025-11-06 06:47:56 +02:00
Protesilaos Stavrou 0cb387926f
emacs: remove needless space in a comment 2025-11-06 06:40:10 +02:00
Protesilaos Stavrou 583bb60e8b
emacs: stash changes in case of failure in prot-simple-update-package-repositories-subr 2025-11-06 05:14:05 +02:00
Protesilaos Stavrou 9504718fd7
emacs: check that modus-themes-mixed-fonts is bound for prot/enable-variable-pitch 2025-11-06 05:13:40 +02:00
Protesilaos Stavrou a2f9c302f2
emacs: make window--delete silent about tab closing 2025-11-06 03:32:00 +02:00
Protesilaos Stavrou 1c8a166aaa
emacs: reword a statement to be easier to fill the paragraph 2025-11-06 03:24:38 +02:00
Protesilaos Stavrou 69ddfee51c
emacs: add log-edit hooks 2025-11-05 20:51:38 +02:00
Protesilaos Stavrou 5f5f2a2f58
emacs: make prot-simple-update-some-or-all-of-my-package-repositories more sophisticated 2025-11-05 20:51:03 +02:00
Protesilaos Stavrou 572d39200b
emacs: comment out the code to avoid flash of light and related
I need to review all this because a lot has changed since I last used it.
2025-11-04 12:23:23 +02:00
Protesilaos Stavrou 3ee0c668cc
emacs: ensure prot-simple-kill-buffer-dwim kills the correct buffer when closing the tab 2025-11-04 05:03:46 +02:00
Protesilaos Stavrou bdbcf731f3
emacs: define key map for macroexpand mode and make it fontify semantically
Now I have a potential use-case for elisp-fontify-semantically.
2025-11-04 04:04:19 +02:00
Protesilaos Stavrou 639061da76
emacs: include handling of tabs in the "kill dwim" command 2025-11-04 03:46:23 +02:00
Protesilaos Stavrou 238a7a04d9
emacs: make sure cursory restores its preset, as the order of calls matters 2025-11-02 11:53:55 +02:00
Protesilaos Stavrou d4eb5916b0
emacs: simplify the notmuch-fcc-dirs 2025-11-02 11:12:39 +02:00
Protesilaos Stavrou 118c2d6781
emacs: remove link to agitate section 2025-11-02 09:22:52 +02:00
Protesilaos Stavrou c6d236d88f
emacs: also implement tab-name for magit display-buffer-alist 2025-11-02 09:21:48 +02:00
Protesilaos Stavrou 7f13fbe6a1
emacs: display-buffer-alist shall reuse the magit tab, if available 2025-11-02 09:19:39 +02:00
Protesilaos Stavrou e55b11035f
emacs: implement display-buffer-alist inhibit-switch-frame 2025-11-02 09:19:22 +02:00
Protesilaos Stavrou 207f425eb4
emacs: fix critical typo in the Magit configuration block 2025-11-02 07:21:48 +02:00
Protesilaos Stavrou 6a90ec55e6
emacs: make Magit use display-buffer and handle magit-status in display-buffer-alist 2025-11-02 07:04:50 +02:00
Protesilaos Stavrou d77347f84e
emacs: define prot-register.el and use it 2025-11-02 06:54:49 +02:00
Protesilaos Stavrou 037571bc95
emacs: update the rcirc configuration 2025-11-02 06:54:10 +02:00
Protesilaos Stavrou 04fb4ce1f5
emacs: make small rewording about package.el 2025-11-01 20:36:05 +02:00
Protesilaos Stavrou 6235fb1abe
emacs: BREAKING stop using use-package for icons, web, and which-key 2025-11-01 20:34:34 +02:00
Protesilaos Stavrou 88875a3082
emacs: BREAKING stop using use-package for notmuch and related 2025-11-01 20:15:44 +02:00
Protesilaos Stavrou f017710070
emacs: add prot-emacs-configure to a few more places
Just to be sure I catch any errors with thoses.
2025-11-01 20:15:13 +02:00
Protesilaos Stavrou 545f9aaa51
emacs: BREAKING stop using use-package for denote and related 2025-11-01 19:28:15 +02:00
Protesilaos Stavrou 70810c629d
emacs: BREAKING stop using use-package for agitate 2025-11-01 18:44:29 +02:00
Protesilaos Stavrou 24b5855cec
emacs: BREAKING stop using use-package for dictionary 2025-11-01 18:44:23 +02:00
Protesilaos Stavrou e9e7457acc
emacs: BREAKING stop using use-package for outline 2025-11-01 18:44:09 +02:00
Protesilaos Stavrou 1853d1e8ad
emacs: BREAKING stop using use-package for flymake 2025-11-01 18:41:40 +02:00
Protesilaos Stavrou 63d25142af
emacs: BREAKING stop using use-package for common languages and spell checking 2025-11-01 18:38:31 +02:00
Protesilaos Stavrou 6d60de0847
emacs: BREAKING stop using use-package for show-paren-mode 2025-11-01 18:26:52 +02:00
Protesilaos Stavrou 3892f3e078
emacs: BREAKING stop using use-package for electric 2025-11-01 18:26:44 +02:00
Protesilaos Stavrou 69d06fd066
emacs: BREAKING stop using use-package for elisp and basics for tabs 2025-11-01 18:23:53 +02:00
Protesilaos Stavrou e0ff3b3bae
emacs: BREAKING stop using use-package for Org 2025-11-01 18:18:18 +02:00
Protesilaos Stavrou 02c19b2c13
emacs: BREAKING stop using use-package for calendar and appt 2025-11-01 18:03:04 +02:00
Protesilaos Stavrou 2a72a56c3d
emacs: move the VC keybindings a bit further down 2025-11-01 18:00:56 +02:00
Protesilaos Stavrou a3f396a9ab
emacs: check for display-graphic-p a bit later
Otherwise it fails to do the right thing in the early-init.el.
2025-11-01 18:00:29 +02:00
Protesilaos Stavrou 1fb362e22b
emacs: BREAKING stop using use-package for prot-modeline 2025-11-01 17:54:26 +02:00
Protesilaos Stavrou ea09222488
emacs: remove an outdated comment 2025-11-01 17:54:13 +02:00
Protesilaos Stavrou 7d86e9641d
emacs: emacs: BREAKING stop using use-package for agitate (but comment it out for now)
I am commenting it out because I might not need it anymore. Or,
anyway, there may be parts that are now covered by core Emacs.
2025-11-01 17:53:30 +02:00
Protesilaos Stavrou 8f23150cfb
emacs: evaluate display-graphic-p only once 2025-11-01 17:47:40 +02:00
Protesilaos Stavrou 36454ec12c
emacs: BREAKING stop using use-package for vc and related 2025-11-01 14:39:12 +02:00
Protesilaos Stavrou 6c4800ef0f
emacs: BREAKING stop using use-package for project 2025-11-01 14:30:32 +02:00
Protesilaos Stavrou 2233458a40
emacs: BREAKING stop using use-package for winner, undelete-frame, etc. 2025-11-01 14:28:40 +02:00
Protesilaos Stavrou c510b8742f
emacs: BREAKING stop using use-package for beframe 2025-11-01 14:25:12 +02:00
Protesilaos Stavrou 0c48e7e5a0
emacs: BREAKING stop using use-package for prot-window and related 2025-11-01 14:23:27 +02:00
Protesilaos Stavrou 48b66007fd
emacs: fix a loading problem with dired 2025-11-01 14:23:16 +02:00
Protesilaos Stavrou 4c1846ce84
emacs: BREAKING stop using use-package for dired and related 2025-11-01 14:14:48 +02:00
Protesilaos Stavrou 80496c781e
emacs: install keycast before checking for other configurations 2025-11-01 14:14:32 +02:00
Protesilaos Stavrou 971bc2bed7
emacs: BREAKING stop using use-package for grep, xref, and re-builder 2025-11-01 14:02:56 +02:00
Protesilaos Stavrou f6cc3e2cfc
emacs: require the battery package when needed 2025-11-01 13:57:57 +02:00
Protesilaos Stavrou 52e904d762
emacs: BREAKING stop using use-package for isearch 2025-11-01 13:57:57 +02:00
Protesilaos Stavrou 692d79df1b
emacs: BREAKING stop using use-package for vertico 2025-11-01 13:57:57 +02:00
Protesilaos Stavrou 632e27544d
emacs: autoload prot-shell and prot-shell-mode 2025-11-01 13:33:28 +02:00
Protesilaos Stavrou 283716ab71
emacs: BREAKING stop using use-package for mct 2025-11-01 12:04:17 +02:00
Protesilaos Stavrou 7f6a6f6ed9
emacs: BREAKING stop using use-package for marginalia 2025-11-01 12:01:08 +02:00
Protesilaos Stavrou c8323a9bf4
emacs: BREAKING stop using use-package for embark 2025-11-01 12:00:21 +02:00
Protesilaos Stavrou 3170d1299f
emacs: BREAKING stop using use-package for consult 2025-11-01 11:57:48 +02:00
Protesilaos Stavrou d073448862
emacs: fix some loading problems 2025-11-01 11:57:26 +02:00
Protesilaos Stavrou ca0e2abfef
emacs: BREAKING stop using use-package for corfu 2025-11-01 11:46:32 +02:00
Protesilaos Stavrou 3e49eaceed
emacs: BREAKING stop using use-package for abbrev 2025-11-01 11:45:01 +02:00
Protesilaos Stavrou 313020dfe3
emacs: BREAKING stop using use-package for dabbrev 2025-11-01 11:43:47 +02:00
Protesilaos Stavrou 0a23c754d1
emacs: BREAKING stop using use-package for savehist 2025-11-01 11:43:19 +02:00
Protesilaos Stavrou 279164b6d4
emacs: BREAKING stop using use-package for common minibuffer settings 2025-11-01 11:42:37 +02:00
Protesilaos Stavrou bb357e3c83
emacs: BREAKING stop using use-package for file-name-shadow and related 2025-11-01 11:40:35 +02:00
Protesilaos Stavrou 626e092f19
emacs: BREAKING stop using use-package for minibuffer-electric-default-mode 2025-11-01 11:39:42 +02:00
Protesilaos Stavrou 1e3505e52a
emacs: BREAKING stop using use-package for minibuffer-depth-indicate-mode 2025-11-01 11:38:50 +02:00
Protesilaos Stavrou f14f12ea57
emacs: BREAKING stop using use-package for completion styles 2025-11-01 11:38:03 +02:00
Protesilaos Stavrou 764ccb50be
emacs: make small refinements to when two minor modes are loaded 2025-11-01 11:35:44 +02:00
Protesilaos Stavrou 444ba41946
emacs: BREAKING stop using use-package for keycast 2025-11-01 11:32:54 +02:00
Protesilaos Stavrou 6a514d0ed3
emacs: BREAKING stop using use-package for which-func 2025-11-01 11:31:50 +02:00
Protesilaos Stavrou 2018105225
emacs: BREAKING stop using use-package for display-battery-mode 2025-11-01 11:30:54 +02:00
Protesilaos Stavrou 9de641952c
emacs: BREAKING stop using use-package for shell 2025-11-01 11:30:14 +02:00
Protesilaos Stavrou 82f20c2c51
emacs: BREAKING stop using use-package for compile 2025-11-01 11:28:41 +02:00
Protesilaos Stavrou a0f1c743d0
emacs: BREAKING stop using use-package for comint 2025-11-01 11:27:34 +02:00
Protesilaos Stavrou cf8506c9f3
emacs: BREAKING stop using use-package for password-store 2025-11-01 11:26:51 +02:00
Protesilaos Stavrou 828c141916
emacs: BREAKING stop using use-package for tmr 2025-11-01 11:25:39 +02:00
Protesilaos Stavrou eb8b09676e
emacs: BREAKING stop using use-package for goto-chg 2025-11-01 11:23:29 +02:00
Protesilaos Stavrou 77562dac15
emacs: BREAKING stop using use-package for substitute 2025-11-01 11:22:12 +02:00
Protesilaos Stavrou 090c0c2529
emacs: BREAKING stop using use-package for the server 2025-11-01 11:17:56 +02:00
Protesilaos Stavrou b2b1fe8318
emacs: BREAKING stop using use-package for proced 2025-11-01 11:17:24 +02:00
Protesilaos Stavrou 5242398681
emacs: BREAKING stop using use-package for manpages 2025-11-01 11:16:58 +02:00
Protesilaos Stavrou 54157d3b57
emacs: BREAKING stop using use-package for world-clock 2025-11-01 11:16:10 +02:00
Protesilaos Stavrou 66831920cf
emacs: BREAKING stop using use-package for display-time-mode 2025-11-01 11:15:37 +02:00
Protesilaos Stavrou c60d4abd08
emacs: BREAKING stop using use-package for tooltips 2025-11-01 11:14:54 +02:00
Protesilaos Stavrou 8b81b7201c
emacs: BREAKING stop using use-package for delete-selection-mode 2025-11-01 11:14:19 +02:00
Protesilaos Stavrou 539b2de37d
emacs: BREAKING stop using use-package for autorevert 2025-11-01 11:13:33 +02:00
Protesilaos Stavrou fa6a0ad5fb
emacs: BREAKING stop using use-package for registers 2025-11-01 11:10:32 +02:00
Protesilaos Stavrou c779d34fcf
emacs: BREAKING stop using use-package for bookmarks 2025-11-01 11:10:26 +02:00
Protesilaos Stavrou c569394ceb
emacs: BREAKING stop using use-package for repeat-mode 2025-11-01 11:10:17 +02:00
Protesilaos Stavrou 7983aa2f93
emacs: BREAKING stop using use-package for the mouse 2025-11-01 11:06:28 +02:00
Protesilaos Stavrou e7911b9da1
emacs: BREAKING stop using use-package for recentf 2025-11-01 11:05:13 +02:00
Protesilaos Stavrou af5fa0cd68
emacs: BREAKING stop using use-package for prot-prefix 2025-11-01 11:03:24 +02:00
Protesilaos Stavrou 564f5f075d
emacs: BREAKING stop using use-package for prot-comment 2025-11-01 10:50:03 +02:00
Protesilaos Stavrou eae0a6dc00
emacs: BREAKING stop using use-package for prot-pair 2025-11-01 10:49:45 +02:00
Protesilaos Stavrou 92a1d1d8d1
emacs: introduce the prot-emacs-autoload macro 2025-11-01 10:45:57 +02:00
Protesilaos Stavrou 07bc2bc009
emacs: BREAKING stop using use-package for prot-scratch 2025-11-01 10:31:49 +02:00
Protesilaos Stavrou 38ec49950a
emacs: BREAKING stop using use-package for prot-simple 2025-11-01 10:28:46 +02:00
Protesilaos Stavrou 40ba0f5af2
emacs: BREAKING stop using use-package for prot-common 2025-11-01 10:24:34 +02:00
Protesilaos Stavrou 7610a9132c
emacs: BREAKING stop using use-package for essential configurations 2025-11-01 10:22:40 +02:00
Protesilaos Stavrou cc4089023e
emacs: BREAKING stop using use-package for face-remap 2025-11-01 10:19:46 +02:00
Protesilaos Stavrou 6558303f45
emacs: BREAKING stop using use-package for show-font 2025-11-01 10:15:55 +02:00
Protesilaos Stavrou 6e5b083f35
emacs: BREAKING stop using use-package for fontaine 2025-11-01 10:12:13 +02:00
Protesilaos Stavrou 4ede8e09a2
emacs: BREAKING stop using use-package for theme-buffet 2025-11-01 10:05:29 +02:00
Protesilaos Stavrou fcb490c15d
emacs: BREAKING stop using use-package for cursory 2025-11-01 10:01:45 +02:00
Protesilaos Stavrou 53efa5524a
emacs: BREAKING stop using use-package for rainbow-mode 2025-11-01 09:59:41 +02:00
Protesilaos Stavrou 5c17562d50
emacs: BREAKING stop using use-package for spacious-padding 2025-11-01 09:56:16 +02:00
Protesilaos Stavrou 2cf0ee25af
emacs: BREAKING stop using use-package for lin 2025-11-01 09:54:22 +02:00
Protesilaos Stavrou 81a250b2af
emacs: BREAKING stop using use-package for pulsar 2025-11-01 09:53:08 +02:00
Protesilaos Stavrou 82db0b17c8
emacs: declare the desired indent for prot-emacs-configure 2025-11-01 09:52:31 +02:00
Protesilaos Stavrou d6ab3d6391
emacs: use prot-emacs-configure for the packages that I already moved away from use-package 2025-10-31 07:09:34 +02:00
Protesilaos Stavrou 6d308bd741
emacs: BREAKING stop using use-package for the standard-themes 2025-10-31 07:08:41 +02:00
Protesilaos Stavrou d18bb05db4
emacs: make small tweaks to the modus-themes configuration 2025-10-31 07:08:41 +02:00
Protesilaos Stavrou 8d9669fa72
emacs: BREAKING stop using use-package for the doric-themes 2025-10-31 07:08:41 +02:00
Protesilaos Stavrou 367c8bd74a
emacs: BREAKING stop using use-package for the ef-themes 2025-10-31 07:08:41 +02:00
Protesilaos Stavrou c9c0ff9fad
emacs: BREAKING stop using use-package for the modus-themes 2025-10-31 07:08:40 +02:00
Protesilaos Stavrou f8bb046db9
emacs: write the prot-emacs-configure macro 2025-10-31 07:08:05 +02:00
Protesilaos Stavrou eef91be1a8
emacs: reorder the conditions of prot-emacs-load-theme-family 2025-10-31 06:48:02 +02:00
Protesilaos Stavrou d4a71d7147
emacs: make small rewordings to the prot-emacs.org 2025-10-31 06:44:52 +02:00
Protesilaos Stavrou 995c4973f7
emacs: reword a comment in the init.el for clarity 2025-10-31 06:38:59 +02:00
Protesilaos Stavrou da9a1746ca
emacs: remove the extra fontification for my package macros
I do not rely on it, anyway, and there is no reason to mess around
with font-lock with something as trivial as this.
2025-10-31 06:31:09 +02:00
Protesilaos Stavrou 73972a1baf
emacs: redo the prot-emacs-install to do nothing with existing packages 2025-10-31 06:27:54 +02:00
Protesilaos Stavrou 1f4a576386
emacs: write the prot-emacs-hook macro 2025-10-31 05:59:30 +02:00
Protesilaos Stavrou 9cdfd710cf
emacs: write the prot-emacs-install macro 2025-10-31 05:55:24 +02:00
Protesilaos Stavrou 49cb449c6d
emacs: do all theme testing without use-package
I had this strange issue where the local files would not be loaded as
intended.
2025-10-31 04:23:31 +02:00
Protesilaos Stavrou 1cf6d288e3
emacs: stop using log-edit-mode for git-commit-major-mode
I thought I would benefit from it, but it turns out I do not do
anything differently. Better keep it simple and see how it works this
way.
2025-10-30 18:19:24 +02:00
Protesilaos Stavrou 10f4e8ea83
emacs: use variable-pitch-mode only when modus-themes-mixed-fonts is set 2025-10-30 07:13:44 +02:00
Protesilaos Stavrou 4879ca1dd0
emacs: update the code used to test modus-themes, ef-themes, standard-themes 2025-10-29 12:54:26 +02:00
Protesilaos Stavrou fd97d2cd65
emacs: make which-function-mode omit keyword from current Elisp definition 2025-10-29 12:46:33 +02:00
Protesilaos Stavrou 8294ad7cf7
emacs: simplify how the nerd-icons are loaded 2025-10-28 11:38:46 +02:00
Protesilaos Stavrou 641cc998d4
emacs: tweak how the nerd icons are loaded 2025-10-28 09:49:29 +02:00
Protesilaos Stavrou 27f3626283
emacs: rebind C-x C-r to recentf-open
This is a more useful key command. Plus, I load recentf-mode only when
I need it.
2025-10-28 06:00:13 +02:00
Protesilaos Stavrou ae02f3df79
emacs: make prot/which-function ignore errors for its lisp-data-mode routine 2025-10-28 05:55:50 +02:00
Protesilaos Stavrou 120cfc8d05
emacs: remove erroneous quote in prot-modeline-which-function-indicator 2025-10-28 05:55:23 +02:00
Protesilaos Stavrou b6bae95d9a
emacs: delete the prot-elfeed file and consolidate the relevant configuration 2025-10-28 05:52:52 +02:00
Protesilaos Stavrou cd13d9db4f
emacs: bind prot-comment to M-; as well 2025-10-28 05:38:07 +02:00
Protesilaos Stavrou d82383edeb
emacs: reduce prot-comment-comment-keywords to only those I use 2025-10-28 05:38:07 +02:00
Protesilaos Stavrou 7fa7347cb3
emacs: document the Elfeed package 2025-10-28 05:38:07 +02:00
Protesilaos Stavrou a7735a0ebb
emacs: remove prot-emacs-treesitter-extras user option
It was only used in one place that I no longer have. It also is about
tree-sitter, which I have not used in earnest yet.
2025-10-28 04:50:39 +02:00
Protesilaos Stavrou eda58b87db
emacs: remove packages I am no longer using
I have not relied on them in a very long time. My configuration may be
out of date.
2025-10-28 04:50:39 +02:00
Protesilaos Stavrou 0c22c4b3b5
emacs: capitalise the prot-modeline frame name 2025-10-28 04:41:21 +02:00
Protesilaos Stavrou 7994421309
emacs: add configuration for testing the doric-themes 2025-10-27 19:18:34 +02:00
Protesilaos Stavrou 22b968dfdb
emacs: remove reference to prot-modeline-align-right (deleted it a long time ago) 2025-10-27 18:10:13 +02:00
Protesilaos Stavrou aa0003e3cf
emacs: adjust the face of prot-modeline-which-function-indicator 2025-10-27 18:09:56 +02:00
Protesilaos Stavrou fb2a8a8c1e
emacs: make prot-modeline-indicator-small only have a height value 2025-10-27 18:09:20 +02:00
Protesilaos Stavrou 1219c9ac73
emacs: move the which-function configuration to the modeline module 2025-10-26 07:37:49 +02:00
Protesilaos Stavrou 03ae3b496d
emacs: make further tweaks to the mode line and bring which-function under my control 2025-10-26 07:36:02 +02:00
Protesilaos Stavrou 3fd151c2aa
emacs: EXPERIMENTAL override for which-function 2025-10-26 07:08:03 +02:00
Protesilaos Stavrou f0523f2b36
emacs: make which-function indicator face use a smaller height 2025-10-26 07:07:46 +02:00
Protesilaos Stavrou 38be9ef381
emacs: define frame title indicator for my mode line 2025-10-26 06:20:55 +02:00
Protesilaos Stavrou 0f7e3d7331
emacs: go back to binding only the magit-status command
I tried the other commands. They are useful, but I prefer to work on
my changes through the status buffer. This way I can accumulate
changes and then review/commit them as I see fit.
2025-10-26 05:42:09 +02:00
Protesilaos Stavrou 9575f0a1dc
emacs: fix the initial and default frame alists 2025-10-25 13:04:20 +03:00
Protesilaos Stavrou f8f5cb7549
emacs: tangle the prot-emacs-git to cover the latest tweaks to it 2025-10-25 12:53:43 +03:00
Protesilaos Stavrou f3b86df15b
emacs: simplify the setup for the scroll bars 2025-10-25 12:53:22 +03:00
Protesilaos Stavrou 32102528a7
emacs: make small tweaks to the configuration of Magit 2025-10-25 06:40:18 +03:00
Protesilaos Stavrou 59bf35160b
emacs: move the ediff section further down 2025-10-25 06:39:29 +03:00
Protesilaos Stavrou c2dbbabcea
emacs: add section about smerge-mode 2025-10-25 05:57:06 +03:00
Protesilaos Stavrou 543ffa5dea
emacs: change which-function-mode face and add an experimental custom function for it 2025-10-24 07:26:47 +03:00
Protesilaos Stavrou 83b595c848
emacs: bring the laptop settings in the default frame parameters and enable beframe on the laptop 2025-10-24 05:51:36 +03:00
Protesilaos Stavrou 93ce4a91eb
emacs: enable beframe also on the laptop 2025-10-24 04:21:51 +03:00
Protesilaos Stavrou f0e3214616
emacs: make prot-elisp-eval-and-print-last-sexp handle newlines in its output 2025-10-23 19:37:58 +03:00
Protesilaos Stavrou af1da0575f
emacs: update the testing configuration of the modus-themes 2025-10-23 06:35:33 +03:00
Protesilaos Stavrou f34635ad84
emacs: make prot-elisp-eval-and-print-last-sexp do nothing without an expression
There is no need to prompt for one, as then I might as well just write
it out directly. It was a good exercise though.
2025-10-22 07:45:39 +03:00
Protesilaos Stavrou 04b1016f10
emcs: add back the %S in prot-elisp.el that I had accidentally removed 2025-10-20 06:24:08 +03:00
Protesilaos Stavrou f365606d6d
emacs: define another binding for prot-simple-indent-dwim 2025-10-19 17:54:14 +03:00
Protesilaos Stavrou 434160ecae
emacs: remove a command that I do not need and was only experimenting with 2025-10-19 17:54:14 +03:00
Protesilaos Stavrou 3d5b125c7b
emacs: use "macroexpand" term in the name of my custom command
This way it is more faithful to what it does and I can find it more
easily via M-x.
2025-10-19 17:54:13 +03:00
Protesilaos Stavrou 6f67eebe61
emacs: fix typo in the name of a function 2025-10-19 17:54:13 +03:00
Protesilaos Stavrou 98273390a4
emacs: make sure prot-elisp works with %S representation of the code
Otherwise I do not get strings and such.
2025-10-19 17:54:07 +03:00
Protesilaos Stavrou 9c4751310c
emacs: make prot-elisp-eval-and-print-last-sexp copy to the kill-ring as well 2025-10-15 10:46:08 +03:00
Protesilaos Stavrou cef5d6ed6e
emacs: expand the commentary about the *scratch* buffer 2025-10-14 07:06:57 +03:00
Protesilaos Stavrou 13dd210bc6
emacs: tweak the wording of initial-scratch-message 2025-10-14 06:53:08 +03:00
Protesilaos Stavrou 0d26d2efc1
emacs: define prot-elisp library and configure it 2025-10-14 06:48:25 +03:00
Protesilaos Stavrou fd7d7a7df5
emacs: lower the eldoc-idle-delay 2025-10-14 06:41:43 +03:00
Protesilaos Stavrou bacf0b8d24
emacs: define command to describe the symbol at point 2025-10-14 05:11:28 +03:00
Protesilaos Stavrou ae414f5a01
emacs: redefine key bindings for prot-simple-indent-dwim and completion-at-point' 2025-10-13 07:49:27 +03:00
Protesilaos Stavrou f84b1ffc42
emacs: define prot-simple-indent-dwim command 2025-10-13 07:13:32 +03:00
Protesilaos Stavrou be0f440349
emacs: define basic configuration for emacs-lisp-mode and lisp-interaction-mode 2025-10-13 06:56:42 +03:00
Protesilaos Stavrou 6bad8a36e0
emacs: increase the eldoc-idle-delay 2025-10-13 06:56:20 +03:00
Protesilaos Stavrou 94cc5f190f
emacs: make the command to update my Git repos work with completion by default 2025-10-10 07:12:31 +03:00
Protesilaos Stavrou b9f59a5c53
delight.sh: switch back to Adwaita for the default icon theme
Actually, this should be done on a case-by-case basis (e.g. Adwaita on
Xfce is not good enough, but is fine on GNOME) and/or with a fallback
to Adwaita. But this is okay for now.
2025-10-07 13:01:45 +03:00
Protesilaos Stavrou d71937be0d
delight.sh: make the Emacs function load a newer Modus theme or derivative 2025-10-07 13:01:23 +03:00
Protesilaos Stavrou 5bde162c02
emacs: update the testing code for the Ef and Standard themes 2025-10-06 09:08:59 +03:00
Protesilaos Stavrou 6e558d9692
emacs: add code for testing the themes when needed 2025-10-04 18:55:38 +03:00
Protesilaos Stavrou 31eba792a0
emacs: fix broken link 2025-10-03 09:30:46 +03:00
Protesilaos Stavrou a42cb7b6f1
emacs: update the configurations of the themes now that they build on top of Modus 2025-10-03 09:29:38 +03:00
Protesilaos Stavrou d852d8c1e7
emacs: update the configuration of the ef-themes now that they are derived from the modus-themes 2025-10-02 20:11:15 +03:00
Protesilaos Stavrou a16e7dbf39
Merge branch 'master' of github.com:protesilaos/dotfiles 2025-09-29 15:08:31 +03:00
Protesilaos Stavrou 1622f25243
emacs: rename the prot-emacs-coach command to prot-emacs-meeting
Then it works in every situation.
2025-09-29 15:05:28 +03:00
Protesilaos Stavrou e2117aaa30
emacs: remove the 'setq-default' that are not needed 2025-09-25 20:44:54 +03:00
Protesilaos Stavrou c80e9ffc20
emacs: remove variable that no longer exists in Emacs 2025-09-25 20:42:26 +03:00
Protesilaos Stavrou 233ec940d2
emacs: stop reversing the vertico layout
It works fine most of the time, except when the text is too large and
the contents of the minibuffer consist of candidates with long lines.
There may be other issues as well, but this is the obvious one that I
would encounter frequently while doing 'consult-outline' on, say, my
prot-emacs.org file while using any 'fontaine' preset with a default
font size greater than 'medium'.
2025-09-17 16:45:39 +03:00
Protesilaos Stavrou 1aa17d616a
emacs: make small correction in a prot-emacs.org heading 2025-09-17 16:34:33 +03:00
Protesilaos Stavrou b94ec1f190
emacs: end experiment with Denote custom component order
It works well and I am happy with the results.
2025-09-06 08:39:52 +03:00
Protesilaos Stavrou f64b5e666a
emacs: reverse the Vertico layout 2025-09-03 15:40:52 +03:00
Protesilaos Stavrou 10eb411c85
emacs: reorganise the settings for the generic minibuffer completions 2025-09-03 08:05:26 +03:00
Protesilaos Stavrou 77912e6390
emacs: add missing interactive spec from prot-simple-update-all-my-package-repositories 2025-09-02 18:46:55 +03:00
Protesilaos Stavrou efe77c1c7e
emacs: add command prot-simple-update-all-my-package-repositories 2025-09-01 20:04:45 +03:00
Protesilaos Stavrou a64bae8b78
emacs: set minibuffer-visible-completions everywhere except the file category 2025-08-26 16:03:09 +03:00
Protesilaos Stavrou c29c221e49
emacs: set minibuffer-visible-completions back to nil
The non-nil option breaks the navigation to the given directory when
using find-file.
2025-08-26 15:54:41 +03:00
Protesilaos Stavrou 500e863e5a
emacs: mention project-buffer as one of the known completion categories 2025-08-26 09:13:50 +03:00
Protesilaos Stavrou 6cc746439d
emacs: make nerd-icons-completion show icons only when necessary 2025-08-25 06:57:19 +03:00
Protesilaos Stavrou 8aa9e0db9b
emacs: tweak the format of the custom agenda view for "Prot Asks" 2025-08-23 09:59:09 +03:00
Protesilaos Stavrou eab1cb4c79
emacs: stop using an outline for Org refile 2025-08-23 09:58:56 +03:00
Protesilaos Stavrou 5af0b35cd1
emacs: make tweaks to the generic completion to use completion-eager-display (Emacs 31) 2025-08-23 09:55:35 +03:00
Protesilaos Stavrou b2fb80905d
i3: tweak the theme colours 2025-08-19 07:30:14 +03:00
Protesilaos Stavrou b9ce89fe2d
mbsync: use tlstype as ssltype is deprecated 2025-08-19 07:29:39 +03:00
Protesilaos Stavrou 44e029acba
emacs: fix missing annotation function in prot-eww 2025-08-11 12:39:24 +03:00
Protesilaos Stavrou cf02473d17
emacs: tweak the 'box' cursory preset 2025-08-11 08:54:10 +03:00
Protesilaos Stavrou 3667fd1634
emacs: make dired open files externally as a set 2025-08-11 08:53:14 +03:00
Protesilaos Stavrou 3e6faf505e
emacs: apply the custom faces to Org tags only when using the modus-themes 2025-08-11 08:52:40 +03:00
Protesilaos Stavrou b304b248f1
emacs: use setq-default for olivetti-body-width
It is a buffer-local variable, meaning that 'setq' will only apply to
the current buffer.
2025-08-11 08:51:51 +03:00
Protesilaos Stavrou f4d6099554
emacs: add missing parenthesis 2025-08-03 07:36:37 +03:00
Protesilaos Stavrou ba701f84ae
emacs: expand the beframe configuration with consult-buffer integration 2025-08-03 07:33:47 +03:00
Protesilaos Stavrou c59fbc5ebf
emacs: tweak the Denote setup for automatically renaming relevant buffers 2025-08-03 07:33:28 +03:00
Protesilaos Stavrou ddb1601ce1
emacs: stop using the orderless-flex style
Unlike the completion-styles, these do not work on the basis of "first
matching one is used" but are instead combined. The result is
overlapping matches.
2025-08-03 07:32:21 +03:00
Protesilaos Stavrou d70319903c
emacs: add a flex method to the orderless-matching-styles
I want to experiment with this again.
2025-08-01 08:26:20 +03:00
Protesilaos Stavrou 7ebbd2b13f
emacs: stop using my custom orderless style dispatchers 2025-08-01 08:26:00 +03:00
Protesilaos Stavrou 5651f6d0b3
emacs: fix a typo in org-todo-keywords 2025-08-01 08:25:37 +03:00
Protesilaos Stavrou 87623fb643
i3: remove icons from tabs
I tried it for a while and decided that it is too busy.
2025-07-31 10:01:31 +03:00
Protesilaos Stavrou fdfe028bc0
emacs: fix an error in the name of a parameter for prot-pair.el 2025-07-31 09:19:10 +03:00
Protesilaos Stavrou c944c54547
emacs: make dunstrc use conf-mode 2025-07-31 08:13:51 +03:00
Protesilaos Stavrou 1642baab76
polybar: make right click on volume launch pavucontrol 2025-07-31 08:13:28 +03:00
Protesilaos Stavrou 44ebe1e119
emacs: make general refinements to prot-pair.el 2025-07-31 06:57:29 +03:00
Protesilaos Stavrou 319c7c4cfa
i3: make titlebar text a bit smaller 2025-07-29 15:52:05 +03:00
Protesilaos Stavrou 8b85b51df2
polybar: move the bar back to the bottom and make it smaller again 2025-07-29 15:51:48 +03:00
Protesilaos Stavrou 4977dd62c2
dunst: move notifications back to the bottom right 2025-07-29 15:51:35 +03:00
Protesilaos Stavrou c02dc35517
emacs: make prot-search-outline use forward-line for correctness 2025-07-29 15:43:32 +03:00
Protesilaos Stavrou 4de583af1e
emacs: make small corrections to the prot-simple commands for lines 2025-07-29 15:43:09 +03:00
Protesilaos Stavrou 291bc52a97
dunst: place notification at the top right and tweak their offset 2025-07-29 07:11:44 +03:00
Protesilaos Stavrou 82defd4fab
emacs: define commands to copy line forward/backward 2025-07-29 07:05:35 +03:00
Protesilaos Stavrou 3be98af9df
emacs: make prot-simple-delete-line-backward not err if beginning is out of bounds 2025-07-29 06:47:27 +03:00
Protesilaos Stavrou 7a7f9418ef
emacs: make prot-simple-delete-line not err when end is out of bounds 2025-07-29 06:46:18 +03:00
Protesilaos Stavrou bb4895da05
dunst: use Sans font 2025-07-28 08:51:59 +03:00
Protesilaos Stavrou 81d079d14f
sxhkd: notify when i3 config is reloaded 2025-07-28 08:51:20 +03:00
Protesilaos Stavrou 0c407e45d1
i3: experiment with icons in titles of tabbed layout 2025-07-28 08:46:34 +03:00
Protesilaos Stavrou 7556f16c24
polybar: experiment with bar at the top 2025-07-28 08:43:49 +03:00
Protesilaos Stavrou 24d29bf4b9
polybar: increase the max length of the window title 2025-07-28 08:43:40 +03:00
Protesilaos Stavrou 752b8abe5d
polybar: use Sans font 2025-07-28 08:43:02 +03:00
Protesilaos Stavrou 10ad85e7eb
i3: add link to Pango markup documentation 2025-07-28 08:42:44 +03:00
Protesilaos Stavrou 675c2dbb0a
emacs: tweak the spacious-padding-widths 2025-07-28 08:08:02 +03:00
Protesilaos Stavrou ac7146e3ea
emacs: set focus-follows-mouse to non-nil 2025-07-28 08:07:22 +03:00
Protesilaos Stavrou 24e1d70f27
i3: include sample configuration for title icons 2025-07-28 08:06:22 +03:00
Protesilaos Stavrou f2a86a98ff
i3: use a proportionately spaced font for tab titles 2025-07-28 08:05:53 +03:00
Protesilaos Stavrou 53449e6941
i3: set a tabbed layout as the default 2025-07-28 08:05:36 +03:00
Protesilaos Stavrou bb95521e5e
i3: do not use any gaps by default 2025-07-28 08:05:28 +03:00
Protesilaos Stavrou 8f690b1c2e
i3: refine the theme colours 2025-07-28 07:51:45 +03:00
Protesilaos Stavrou 02e7536918
xresources: revise the font sizes 2025-07-27 17:15:00 +03:00
Protesilaos Stavrou a8b32590d1
polybar: make all bars to inherit from common base 2025-07-27 17:14:11 +03:00
Protesilaos Stavrou ab8d3f6ba0
i3: let windows steal focus across workspaces 2025-07-27 13:33:31 +03:00
Protesilaos Stavrou dafdea73e8
i3: use the correct key for smart borders 2025-07-27 13:33:07 +03:00
Protesilaos Stavrou c4a8c9addb
i3: make the focus window border 2px instead of 1px 2025-07-27 12:11:09 +03:00
Protesilaos Stavrou 9f7fd64618
xorg-twm: tweak how xsetroot specifies the default cursor 2025-07-27 10:55:05 +03:00
Protesilaos Stavrou 9fd79d4154
Xresources: specify the cursor theme 2025-07-27 10:54:56 +03:00
Protesilaos Stavrou 8ae6f172e4
emacs: update the cursory configuration 2025-07-27 10:42:03 +03:00
Protesilaos Stavrou d9a33e8cfc
i3: tweak the default border and gap sizes 2025-07-27 10:40:54 +03:00
Protesilaos Stavrou 48598817fd
sxhkdrc_i3: define keys for next/prev focus 2025-07-27 10:40:37 +03:00
Protesilaos Stavrou 30d02fc85d
sxhkd: stop using scrot and switch to maim 2025-07-27 10:40:19 +03:00
Protesilaos Stavrou 8372c65f09
dunstrc: revise configuration to use latest conventions 2025-07-27 10:39:26 +03:00
Protesilaos Stavrou f9c0366b85
emacs: add new :cursor-color attribute for cursory presets (part of dev package) 2025-07-20 13:18:17 +03:00
Protesilaos Stavrou 563db09ca2
emacs: set the x-underline-at-descent-line only when necessary for spacious-padding 2025-07-20 13:17:47 +03:00
Protesilaos Stavrou ae655436ba
emacs: add new dired-preview-trigger-on-start option 2025-07-20 13:17:19 +03:00
Protesilaos Stavrou 9d9669a398
emacs: tweak the formatting of the date in the "Prot Asks" custom agenda 2025-07-17 08:02:52 +03:00
Protesilaos Stavrou d7d4c239b8
emacs: make small refinements to the Prot Asks custom Org agenda 2025-07-14 08:16:08 +03:00
Protesilaos Stavrou dd9667a0bd
emacs: add "Prot Asks" custom Org agenda 2025-07-13 16:48:48 +03:00
Protesilaos Stavrou c66c19dca7
Merge branch 'master' of github.com:protesilaos/dotfiles 2025-07-13 11:04:05 +03:00
Protesilaos Stavrou 514558ac42
emacs: include section about tab-bar-mode 2025-07-13 11:00:20 +03:00
Protesilaos Stavrou 622446d9c0
emacs: tweak the laptop-specific frame settings 2025-07-13 06:15:05 +03:00
Protesilaos Stavrou f739af6889
Merge branch 'master' of github.com:protesilaos/dotfiles 2025-07-12 07:09:12 +03:00
Protesilaos Stavrou 1bc0374d55
emacs: change some settings to be easier to work on the small laptop screen 2025-07-12 06:50:46 +03:00
Protesilaos Stavrou e4afc8ae10
emacs: add missing new lines to the "Prot Asks" capture template 2025-07-11 08:42:25 +03:00
Protesilaos Stavrou a6153fb69a
emacs: define org-tag-faces 2025-07-11 08:34:37 +03:00
Protesilaos Stavrou 04df00a6b1
emacs: add org-capture template for "Prot Asks" 2025-07-11 08:34:18 +03:00
Protesilaos Stavrou b9881e1cc0
emacs: bind the tmr-prefix-map directly 2025-07-11 08:33:56 +03:00
Protesilaos Stavrou 7c191b0c95
emacs: use nerd-icons for grep and xref as well 2025-07-07 07:47:37 +03:00
Protesilaos Stavrou b977a9d045
emacs: revise how the grep/ripgrep search program is applied 2025-07-07 07:47:18 +03:00
Protesilaos Stavrou 531ff4a6f1
emacs: update the mct configuration with new user option 2025-07-06 17:55:14 +03:00
Protesilaos Stavrou 2cf4942d9d
emacs: split the completion-category-overrides into a separate heading 2025-07-04 13:59:59 +03:00
Protesilaos Stavrou c11483ddf2
delight.sh: switch icon theme to Papirus 2025-07-02 10:36:50 +03:00
Protesilaos Stavrou 11781253d5
emacs: add the Emacs 31 variable vc-dir-save-some-buffers-on-revert 2025-07-02 10:36:21 +03:00
Protesilaos Stavrou c5f3c426b8
emacs: set the xref-search-program at the correct time 2025-07-02 10:35:18 +03:00
Protesilaos Stavrou f9d0d41c89
emacs: enable line truncation in grep-mode buffers 2025-07-02 10:34:50 +03:00
Protesilaos Stavrou ca91932a3f
emacs: reorganise some of the basic settings 2025-07-02 10:34:33 +03:00
Protesilaos Stavrou 870651968b
emacs: define separate sections for comint and compile 2025-06-23 08:16:35 +03:00
Protesilaos Stavrou 3ef8cc85b5
emacs: clarify that M-x shell supports bookmarking in Emacs 31 2025-06-23 07:48:53 +03:00
Protesilaos Stavrou 8c875be390
emacs: make small tweak to the spacing of M-x shell config 2025-06-23 07:48:15 +03:00
Protesilaos Stavrou b960658234
emacs: add prot-ediff with some extensions for ediff 2025-06-21 09:04:12 +03:00
Protesilaos Stavrou 0f85e7bf10
emacs: make RET visit Org link 2025-06-16 16:31:52 +03:00
Protesilaos Stavrou d0240161dd
emacs: make compile command use completion 2025-06-14 07:30:07 +03:00
Protesilaos Stavrou 9d35b79f74
emacs: make prot-window-coach scratch buffer also include initial message 2025-06-14 07:29:44 +03:00
Protesilaos Stavrou 97e8e6ab4b
emacs: reposition prot-simple--mark 2025-06-10 13:36:15 +03:00
Protesilaos Stavrou a9c6e8c700
emacs: do not try to align tags in org agenda
I am already doing this for headings. It looks more consistent with
variable-pitch fonts.
2025-06-10 13:35:31 +03:00
Protesilaos Stavrou 6699892497
emacs: update configuration for spacious padding subtle lines 2025-06-10 13:34:59 +03:00
Protesilaos Stavrou ab15540c61
emacs: update the path to the emacslient binary in the .desktop entry 2025-06-09 16:13:24 +03:00
Protesilaos Stavrou 91135aac55
emacs: bind next/previous motion to org agenda key map 2025-06-09 08:00:14 +03:00
Protesilaos Stavrou 846ae00d1c
emacs: streamline the org-todo-keywords 2025-06-09 07:59:12 +03:00
Protesilaos Stavrou 16401e6b8b
emacs: add prefix FORCE argument to prot-org-id-headlines-readable 2025-06-09 07:56:07 +03:00
Protesilaos Stavrou f3042d0ee9
emacs: add outline-regexp to the Org agenda 2025-06-09 07:54:20 +03:00
Protesilaos Stavrou 1446fbbbb3
emacs: make rainbow-mode use :inverse-video
This is the better design for me, as it works with hl-line-mode.
Otherwise, hl-line-mode overrides the background colour on display.
2025-06-09 07:52:42 +03:00
Protesilaos Stavrou 04ab537fff
emacs: mention "scroll behaviour" in the relevant heading 2025-06-09 07:52:22 +03:00
Protesilaos Stavrou 6d591067d3
emacs: use the correct command to transpose window layout 2025-06-09 07:51:56 +03:00
Protesilaos Stavrou d4e59ca6e5
emacs: make it explicit to not start the server if the daemon is running 2025-05-31 07:51:34 +03:00
Protesilaos Stavrou c60a02b20a
emacs: update the mu4e load path to what current Debian is using
I am not relying on this setup. It just is the correct design for my
current installation.
2025-05-27 16:34:19 +03:00
Protesilaos Stavrou b429a4bad2
emacs: update doric-themes package to use the correct configuration 2025-05-27 16:33:53 +03:00
Protesilaos Stavrou 9136f32428
emacs: change default frame width 2025-05-27 16:33:26 +03:00
Protesilaos Stavrou 3aee8becf0
emacs: set diff-font-lock-syntax to nil 2025-05-25 13:01:21 +03:00
Protesilaos Stavrou 5e2a9a73c8
emacs: remove needless code from doric themes module 2025-05-22 20:27:35 +03:00
Protesilaos Stavrou 3f2db180f4
emacs: add missing 'provide' for the doric themes module 2025-05-22 15:23:17 +03:00
Protesilaos Stavrou 3386ea3aec
emacs: make minor tweak to the fontaine presets 2025-05-22 10:50:45 +03:00
Protesilaos Stavrou e4246c209a
emacs: refine the keycast setting for mouse events 2025-05-22 10:49:13 +03:00
Protesilaos Stavrou 4a450bf1de
emacs: update the completion-category-overrides with optional eager-display logic 2025-05-22 10:49:13 +03:00
Protesilaos Stavrou 94339026c8
emacs: use built-in crm-prompt instead of advising completing-read-multiple 2025-05-22 10:49:13 +03:00
Protesilaos Stavrou 9985faa8e1
emacs: make generic completions work closer to my expectations 2025-05-22 10:49:13 +03:00
Protesilaos Stavrou b3d84ba596
emacs: use corfu only when prot-emacs-completion-ui is set
Put differently, if I am using the generic UI I probably am testing
something and thus I want to get that interface also for in-buffer
completions.
2025-05-22 10:49:13 +03:00
Protesilaos Stavrou 9480f035b1
emacs: trunctate lines in embark-collect-mode 2025-05-22 10:49:13 +03:00
Protesilaos Stavrou 83f6e98c4a
emacs: tweak the denote-rename-buffer-format 2025-05-22 10:49:13 +03:00
Protesilaos Stavrou 4e69ce173f
emacs: tweak the magit-section-visibility-indicator 2025-05-22 10:49:13 +03:00
Protesilaos Stavrou 292083a89a
emacs: stop using breadcrumb package and rely on built-in which-function-mode
Breadcrumb provides more detailed information, though I realised I was
not relying on it. All I need is to know which definition I am
operating at. Having this in the mode line also looks cleaner to me.
2025-05-22 10:49:13 +03:00
Protesilaos Stavrou 68b828f57b
emacs: include newlines in whitespace-mode 2025-05-22 10:49:13 +03:00
Protesilaos Stavrou baac18df51
emacs: make minor refinements to prot-dired.el 2025-05-22 10:49:12 +03:00
Protesilaos Stavrou 10d4a710cc
emacs: tweak the elfeed-search-date-format 2025-05-22 10:49:12 +03:00
Protesilaos Stavrou c1c1f5a027
emacs: incorporate my new doric-themes 2025-05-22 10:49:12 +03:00
Protesilaos Stavrou 803ed35311
emacs: tweak how frame parameters are set and enable scroll bars 2025-05-22 10:49:12 +03:00
Protesilaos Stavrou ca46323d3b
emacs: update frame-inhibit-implied-resize to work the way it always did 2025-05-22 07:34:28 +03:00
Protesilaos Stavrou 3b719e5582
emacs: use correct symbol for ef-themes-to-rotate 2025-04-29 13:07:48 +03:00
Protesilaos Stavrou 820edb6e22
emacs: lower the gc-cons-threshold 2025-04-29 07:10:55 +03:00
Protesilaos Stavrou 9c9c46b44b
emacs: update tmr configuration to have only the keys I use 2025-04-26 09:28:29 +03:00
Protesilaos Stavrou 6e33b18025
emacs: add custom value of 'denote-file-name-components-order' for an experiment 2025-04-25 07:00:08 +03:00
Protesilaos Stavrou 03923943f5
emacs: make stylistic change to how org-agenda-files are set 2025-04-24 07:15:11 +03:00
Protesilaos Stavrou 8cabe31eb2
emacs: make some refinements to the display-buffer-alist 2025-04-24 07:14:35 +03:00
Protesilaos Stavrou 3384258382
emacs: do not rebind ESC as it breaks Meta 2025-04-24 07:04:47 +03:00
Protesilaos Stavrou 4e3a04b75d
emacs: also bind prot-simple-keyboard-quit-dwim to ESC
I had the other there thinking I might need the ESC as a Meta
sometimes, but have not used it once. We are good to go.
2025-04-23 14:35:17 +03:00
Protesilaos Stavrou 766de82a37
emacs: link between calendar and denote-journal 2025-04-13 09:24:46 +03:00
Protesilaos Stavrou 6c6dafec1d
emacs: use "interface" instead of "client" to describe Magit and other small rewordings 2025-04-13 09:24:33 +03:00
Protesilaos Stavrou 7369782125
emacs: make comint buffers (including M-x shell) truncate long lines 2025-04-13 09:22:43 +03:00
Protesilaos Stavrou 7961750052
emacs: make stylistic tweaks to pulsar configuration 2025-04-12 19:48:40 +03:00
Protesilaos Stavrou 13057efcd1
emacs: define abbrev entries for inserting keys and commands in Org 2025-04-12 19:48:08 +03:00
Protesilaos Stavrou d0c3b071c2
emacs: fix prot-modeline fallback face for the vc state 2025-04-12 19:46:16 +03:00
Protesilaos Stavrou e25e5b8aa9
emacs: add binding to dired-mode-map for dired-preview-mode 2025-04-12 18:17:13 +03:00
Protesilaos Stavrou 9915995240
emacs: change the location of the RSS feeds file 2025-04-12 18:16:55 +03:00
Protesilaos Stavrou 9a7d215464
emacs: REMOVE prot-embark.el, simplify Embark keys, and use C-, and C-. for common tasks 2025-04-10 14:00:27 +03:00
Protesilaos Stavrou 851a90a06e
emacs: make several "kill" or "yank" command wrappers report the command they are 2025-04-10 08:08:01 +03:00
Protesilaos Stavrou 7cd1d0197d
emacs: define prot-simple-kill-region command 2025-04-10 08:07:46 +03:00
Protesilaos Stavrou 6b9144c448
emacs: update pulsar to also highlight region changes 2025-04-10 08:06:53 +03:00
Protesilaos Stavrou df42477f5f
emacs: use default value of dired-preview-display-action-alist 2025-04-10 07:48:27 +03:00
Protesilaos Stavrou 348d7e9bd1
emacs: tweak the dired-preview display-buffer action 2025-04-08 12:54:02 +03:00
Protesilaos Stavrou e35085dbd5
emacs: add experimental denote-journal function to write file without title and front matter 2025-04-07 08:37:43 +03:00
Protesilaos Stavrou 4792c7bec8
emacs: tweak denote-rename-buffer-format with new development value 2025-04-07 08:37:20 +03:00
Protesilaos Stavrou d447241caa
emacs: use display-buffer-alist to select the help buffer and related 2025-04-07 08:36:47 +03:00
Protesilaos Stavrou 9102584338
emacs: update the key bindings of the modus-themes when I am testing them 2025-04-07 07:27:51 +03:00
Protesilaos Stavrou 96d19e1900
emacs: document the rcirc package 2025-04-06 07:08:09 +03:00
Protesilaos Stavrou 34eb6ee089
emacs: update the sources of consult-denote 2025-04-01 08:01:30 +03:00
Protesilaos Stavrou b30f332a18
emacs: remove warning from denote-org 2025-04-01 08:01:24 +03:00
Protesilaos Stavrou 353e5c962f
emacs: remove warning from denote-sequence 2025-04-01 08:01:18 +03:00
Protesilaos Stavrou 8bb9859ca9
emacs: remove warning from denote-markdown 2025-04-01 08:01:10 +03:00
Protesilaos Stavrou 29a3dec05b
emacs: remove warning from denote-silo 2025-04-01 08:00:42 +03:00
Protesilaos Stavrou ebe5db6ab0
emacs: update the section about denote-journal 2025-04-01 07:59:08 +03:00
Protesilaos Stavrou e5b2e99636
emacs: tweak how prot-embark may be loaded 2025-03-31 07:25:16 +03:00
Protesilaos Stavrou 06812fed5a
emacs: make embark-collect and embark-export directly available in the minibuffer 2025-03-31 07:23:31 +03:00
Protesilaos Stavrou 527769e854
Merge branch 'master' of github.com:protesilaos/dotfiles 2025-03-28 07:45:52 +02:00
Protesilaos Stavrou 9e8bd6ca71
emacs: update the commentary about spacious-padding-subtle-mode-line 2025-03-28 07:45:06 +02:00
Protesilaos Stavrou 1a75dd0af4
emacs: disable the modus-themes palette overrides
They are good, but I was experimenting with them anyway. My setup has
to be close to the defaults to always be able to notice any issues.
2025-03-28 07:44:15 +02:00
Protesilaos Stavrou 3ab0747e5d
emacs: rewrite prot-org-select-heading-in-file doc string 2025-03-28 07:43:51 +02:00
Protesilaos Stavrou 3fac49ca7f
emacs: define global minor mode to override some key bindings 2025-03-28 06:47:49 +02:00
Protesilaos Stavrou ddb98ad89e
emacs: tweak the presentation of the world clock 2025-03-28 06:47:26 +02:00
Protesilaos Stavrou bc3e345179
emacs: change key bindings for window rotation 2025-03-28 06:45:33 +02:00
Protesilaos Stavrou 3d1c039280
emacs: add some more links to publications of mine about Emacs and stuff 2025-03-22 15:55:20 +02:00
Protesilaos Stavrou 92281682b0
notmuch: add accidentally removed post-new rule for packages and include them in the counters 2025-03-22 09:19:44 +02:00
Protesilaos Stavrou b205551593
emacs: update the goto-address-mode configuration
Was it always wrong or did the symbols get renamed at some point?
2025-03-20 08:49:39 +02:00
Protesilaos Stavrou 63ee0fc281
xfce: make delight.sh affect the xfce4-terminal more predictably 2025-03-19 08:10:37 +02:00
Protesilaos Stavrou 26c39578ba
emacs: fix prot-simple-transpose-chars at the end of the line 2025-03-18 19:04:24 +02:00
Protesilaos Stavrou 25dbe773b6
xfce: make terminal always conform with system colours 2025-03-16 16:06:50 +02:00
Protesilaos Stavrou ce24c91b3c
xfce: make xfwm theme explicitly hide app icon
This was happening anyway because the menu icons are not provided. But
the intent is more clear this way.
2025-03-16 11:14:13 +02:00
Protesilaos Stavrou b308f1e14c
xfce: make xfwm theme right side consistent with the left one 2025-03-16 10:59:25 +02:00
Protesilaos Stavrou f29160fee9
xfce: fix active colours for the xfwm theme left side 2025-03-16 10:58:57 +02:00
Protesilaos Stavrou 9a6a565aab
xfce: make all xfwm theme borders narrower 2025-03-16 10:58:45 +02:00
Protesilaos Stavrou d81fbcae21
emacs: update prot-emacs-langs.el to reposition denote-sequence 2025-03-15 12:37:55 +02:00
Protesilaos Stavrou 5c0d4194c6
notmuch: improve the pre-new hook comments at the end for archiving 2025-03-15 12:37:30 +02:00
Protesilaos Stavrou abde635eec
emacs: add my denote-sequence package 2025-03-15 08:24:58 +02:00
Protesilaos Stavrou 45297a8c33
emacs: add my denote-silo package 2025-03-15 08:20:43 +02:00
Protesilaos Stavrou 1848864b2a
emacs: add my denote-journal package 2025-03-15 08:14:34 +02:00
Protesilaos Stavrou 314a0cdfb5
emacs: add my denote-markdown package 2025-03-15 08:13:56 +02:00
Protesilaos Stavrou dff48bd46d
emacs: remove a needless link 2025-03-15 08:09:26 +02:00
Protesilaos Stavrou bd36be7355
emacs: add my denote-org package 2025-03-15 08:09:19 +02:00
Protesilaos Stavrou 006ec816de
emacs: add outline heading for consult-denote 2025-03-15 08:08:43 +02:00
Protesilaos Stavrou b9e4f65801
emacs: tweak the configuration of denote 2025-03-15 07:57:56 +02:00
Protesilaos Stavrou ce3883ab88
emacs: make stylistic tweaks to notmuch saved searches 2025-03-15 07:52:43 +02:00
Protesilaos Stavrou f6821d7e2b
mbsync: use SSLType again as TLSType is not backward-compatible 2025-03-14 08:01:52 +02:00
Protesilaos Stavrou 101c8e698c
emacs: update the prot-emacs-my-packages to include all the Denote-related ones 2025-03-14 08:01:40 +02:00
Protesilaos Stavrou e508f6cd80
emacs: make prot-modeline-vc-branch also work with directories and assume Git as fallback 2025-03-11 14:31:01 +02:00
Protesilaos Stavrou 0ceaaf2f99
emacs: fix incorrect symbol in prot-simple-delete-line-backward 2025-03-11 14:30:39 +02:00
Protesilaos Stavrou 97936956e0
emacs: make line delete commands work with empty lines as well 2025-03-08 10:27:59 +02:00
Protesilaos Stavrou f43b7fd8cb
emacs: tweak modus-themes configuration (experimental) 2025-03-08 10:15:09 +02:00
Protesilaos Stavrou 661229165d fixup! emacs: bind duplicate command to C-a ("again" mnemonic) 2025-03-08 10:14:48 +02:00
Protesilaos Stavrou 6f7926deda
emacs: define commands to delete (not kill) line back and forth 2025-03-08 10:14:12 +02:00
Protesilaos Stavrou 2e22785e1d
emacs: bind duplicate command to C-a ("again" mnemonic) 2025-03-08 10:12:40 +02:00
Protesilaos Stavrou 059897dcdf
emacs: define emacsclient.desktop to do what I want
This is because the default will not start my Emacs but instead launch
a daemon process if there is no running instance of Emacs.
2025-03-06 08:20:23 +02:00
Protesilaos Stavrou 77f0e6fd6c
gtk: add custom CSS for the VTE4 terminals as well (affects gnome-console)
I already have the same thing for GTK3 (affects gnome-terminal).
2025-03-06 07:41:08 +02:00
Protesilaos Stavrou c1e7f231a5
delight.sh: revert to Adwaita because it is easier to maintain 2025-03-04 09:22:06 +02:00
Protesilaos Stavrou ba4b8dd0f8
emacs: update the smpt settings and commentary 2025-03-04 07:47:47 +02:00
Protesilaos Stavrou 2b4e4cb4d0
emacs: experiment with dired-preview-display-action-alist on the side 2025-03-04 07:25:39 +02:00
Protesilaos Stavrou e6cfb6c78a
emacs: make hexl-mode truncate lines by default 2025-03-04 07:25:11 +02:00
Protesilaos Stavrou ac477aac8b
delight.sh: simplify the gsettings calls 2025-03-04 06:40:26 +02:00
Protesilaos Stavrou 1cd715586c
notmuch+mbsync: make modification and update comment on how to archive old mail 2025-03-01 08:47:02 +02:00
Protesilaos Stavrou 728fdec6a3
BREAKING: split up systemd stow package to more accurate mbsync and gnupg 2025-03-01 07:39:28 +02:00
Protesilaos Stavrou f4550ecac9
mbsync_remove_uid.sh: add new script 2025-02-26 08:02:36 +02:00
Protesilaos Stavrou b2dbb43f8d
delight.sh: switch back to adw-gtk3 theme
It looks better with all those libadwaita apps.
2025-02-26 07:59:05 +02:00
Protesilaos Stavrou 5b39e2c362
notmuch: tweak the pre-new script 2025-02-26 07:58:52 +02:00
Protesilaos Stavrou 263377359e
notmuch: tweak the post-new script 2025-02-26 07:58:40 +02:00
Protesilaos Stavrou 4aa396b752
mbsync: switch everything to "Near" to treat the remote as the source of truth 2025-02-26 07:58:04 +02:00
Protesilaos Stavrou 0db44356a8
mbsync: use TLSType instead of SSLType 2025-02-26 07:57:24 +02:00
Protesilaos Stavrou f599466217
emacs: make prot-simple-kill-ring-save work even if mark is unset 2025-02-23 08:08:52 +02:00
Protesilaos Stavrou 7323fb1472
emacs: tweak world clock buffer 2025-02-21 09:32:29 +02:00
Protesilaos Stavrou 1aa59cd008
emacs: make small tweak to the wording of a statement 2025-02-21 08:57:51 +02:00
Protesilaos Stavrou 64537b619e
delight.sh: fix capitalisation of "Adwaita-dark" GTK theme 2025-02-21 08:33:29 +02:00
Protesilaos Stavrou f031d4df6b
gnome: add gnome_custom_keys.sh script 2025-02-18 09:56:12 +02:00
Protesilaos Stavrou b52e1e9c31
Merge branch 'master' of github.com:protesilaos/dotfiles 2025-02-16 11:04:51 +02:00
Protesilaos Stavrou 150a2622f4
emacs: remove vertico-specific tweak for prot-window-popup commands
It is a good idea, but ultimately not needed.
2025-02-16 11:04:15 +02:00
Protesilaos Stavrou fa856b2e85
emacs: make prot-vertico-private-exit treat file category specially even with 1 candidate 2025-02-15 18:20:49 +02:00
Protesilaos Stavrou 0e0fe81a58
Merge branch 'master' of github.com:protesilaos/dotfiles 2025-02-15 07:43:24 +02:00
Protesilaos Stavrou 487851e9cc
emacs: update the eww configuration 2025-02-15 06:59:35 +02:00
Protesilaos Stavrou c3027b39c8
emacs: rewrite prot-eww.el 2025-02-15 06:59:04 +02:00
Protesilaos Stavrou c965be11cd
emacs: add settings and commentary about org-indent-mode 2025-02-13 08:23:23 +02:00
Protesilaos Stavrou e644880554
emacs: set show-font-display-buffer-action-alist to full frame 2025-02-12 12:41:32 +02:00
Protesilaos Stavrou 7b7d9c98b2
Merge branch 'master' of github.com:protesilaos/dotfiles 2025-02-12 08:30:15 +02:00
Protesilaos Stavrou 21d4b7ad04
emacs: make magit use nerd icons where relevant 2025-02-12 08:29:55 +02:00
Protesilaos Stavrou f4f12d05f6
emacs: always prefer horizontal (widescreen) splits 2025-02-12 08:19:31 +02:00
Protesilaos Stavrou 8658ce09e4
emacs: make the prot-window popups also use Vertico buffer mode (see commit 402a4a94) 2025-02-12 08:12:19 +02:00
Protesilaos Stavrou 8d49c9a301
delight.sh: fix sed call for xfce-terminal ColorPalette 2025-02-09 11:36:23 +02:00
Protesilaos Stavrou 91c9fd4f2f
Update all copyright notices 2025-02-09 11:34:53 +02:00
Protesilaos Stavrou da070219a7
redshift: delete it because I am not using it 2025-02-09 11:28:36 +02:00
Protesilaos Stavrou 6b63759d33
herbstluftwm: add rule to float prot-window-popup windows (per 402a4a94) 2025-02-09 10:00:08 +02:00
Protesilaos Stavrou 402a4a9472
emacs+bspwm: refine how prot-window-popup is done, include project switch, and integrate with BSPWM 2025-02-09 09:53:17 +02:00
Protesilaos Stavrou adf40640d2
emacs: update prot-emacs-theme.el to include some missing changes 2025-02-09 09:51:56 +02:00
Protesilaos Stavrou 15d373589f
mpv: fix issue with window not responding to the window manager "quit" 2025-02-07 15:10:12 +02:00
Protesilaos Stavrou 564249c2f2
bspwm: experiment with a split ratio of 0.33 2025-02-07 13:08:06 +02:00
Protesilaos Stavrou 2ebcb80a90
mpv: disable most key bindings 2025-02-07 13:07:35 +02:00
Protesilaos Stavrou 09553057af
gtk: add padding around a VTE terminal (used by xfce4-terminal) 2025-02-07 13:05:09 +02:00
Protesilaos Stavrou 3bf774d41f
sxhkd: simplify the Super + Return binding 2025-02-07 08:29:14 +02:00
Protesilaos Stavrou a08f7e5108
delight.sh: make two small tweaks to avoid potential issues 2025-02-07 08:28:48 +02:00
Protesilaos Stavrou 07ad6b0230
delight.sh: add support for xfce4-terminal 2025-02-07 08:28:04 +02:00
Protesilaos Stavrou cb7a2aa23b
xfce: add configuration for xfce4-terminal 2025-02-07 08:27:44 +02:00
Protesilaos Stavrou 660c3f6a01
Merge branch 'master' of github.com:protesilaos/dotfiles 2025-02-05 08:00:08 +02:00
Protesilaos Stavrou 295b805899
emacs: update web-related settings (WORK-IN-PROGRESS) 2025-02-05 07:59:40 +02:00
Protesilaos Stavrou 088d0ef864
emacs: document the section about browser cookies 2025-02-05 07:59:09 +02:00
Protesilaos Stavrou 03267d7db0
emacs: use the new optional function for dired-preview-display-action-alist (part of dev) 2025-02-05 07:58:29 +02:00
Protesilaos Stavrou f993923e1d
emacs: update font settings to include my new Aporetic fonts
I wrote about it here: <https://protesilaos.com/codelog/2025-02-04-aporetic-fonts-1-0-0/>.
2025-02-05 07:57:49 +02:00
Protesilaos Stavrou 2abb9fbf24
emacs: tangle files I forgot to do earlier 2025-02-05 07:46:53 +02:00
Protesilaos Stavrou 3757186ff4
Merge branch 'master' of github.com:protesilaos/dotfiles 2025-02-05 07:46:22 +02:00
Protesilaos Stavrou 391ba33b17
emacs: stop customising the marginalia-annotator-registry 2025-02-05 07:29:58 +02:00
Protesilaos Stavrou 295da28380
fontconfig: update to my Aporetic fonts
Read: <https://protesilaos.com/codelog/2025-02-04-aporetic-fonts-1-0-0/>.
2025-02-04 12:24:43 +02:00
Protesilaos Stavrou 1bb50db9c4
emacs: document the goto-addr section 2025-02-03 07:51:17 +02:00
Protesilaos Stavrou 06cff6fa39
emacs: document the browse-url section 2025-02-03 07:48:28 +02:00
Protesilaos Stavrou be6f0ae72d
emacs: document the section about SHR 2025-02-03 07:32:28 +02:00
Protesilaos Stavrou fb169161c2
emacs: add comment about not using #+include for sample msmtp config 2025-02-03 07:16:51 +02:00
Protesilaos Stavrou db949f4220
gtk: use Adwaita theme everywhere
It is easier to have this as the default, because it is available on
all systems.
2025-01-30 10:45:06 +02:00
Protesilaos Stavrou cd146f66bc
emacs: add command for readable Org heading CUSTOM_ID 2025-01-30 10:43:53 +02:00
Protesilaos Stavrou f92932d8a3
emacs: use emoji with the notmuch-indicator 2025-01-26 17:00:09 +02:00
Protesilaos Stavrou 8651a23f27
polybar: include seconds in the clock 2025-01-26 16:58:57 +02:00
Protesilaos Stavrou e8543aae3b
emacs: add hook for fundamental-mode 2025-01-26 11:35:38 +02:00
Protesilaos Stavrou 7c0295b115
emacs: include show-font-tabulated in the :commands configuration 2025-01-26 11:35:18 +02:00
Protesilaos Stavrou a2b1fd5222
xorg-twm: set key for switching keyboard layouts and make adjustments 2025-01-26 11:23:02 +02:00
Protesilaos Stavrou 0274e05301
emacs: add prot-simple-sudo command 2025-01-26 08:27:20 +02:00
Protesilaos Stavrou f399c55e45
emacs: tweak the weights of the 'small' fontaine preset 2025-01-26 08:26:57 +02:00
Protesilaos Stavrou d16ad4d8d5
emacs: make the split-height-threshold a bit higher
This is because when I use tiny fonts I still want to be within the
expected boundaries.
2025-01-21 08:32:10 +02:00
Protesilaos Stavrou 2de462769c
emacs: stop using vundo
I have not been relying on it.
2025-01-21 08:30:14 +02:00
Protesilaos Stavrou c94975a985
herbstluftwm: tweak the colour of the current tab background 2025-01-19 10:49:13 +02:00
Protesilaos Stavrou cf6dc5f641
herbstluftwm: make tab height a bit taller 2025-01-19 10:48:44 +02:00
Protesilaos Stavrou ea1b1f7937
emacs: add small and tiny presets for Fontaine 2025-01-19 08:00:50 +02:00
Protesilaos Stavrou 4710d69137
herbstluftwm: tweak the background colour of the current tab 2025-01-18 11:34:56 +02:00
Protesilaos Stavrou 3acbe8a5fe
xresources: tweak xterm font sizes 2025-01-18 11:34:21 +02:00
Protesilaos Stavrou c757173b5a
herbstluftwm: make default border widths a bit smaller 2025-01-18 11:17:12 +02:00
Protesilaos Stavrou fe20afc46c
xorg-twm: make the polybar font size and height a little bit smaller 2025-01-18 11:05:25 +02:00
Protesilaos Stavrou 38e5246c12
herbstluftwm: tweak the size of tab headings 2025-01-18 11:02:39 +02:00
Protesilaos Stavrou a978eb3611
emacs: tweak the 'small' and 'live-stream' fontaine presets 2025-01-18 10:57:51 +02:00
Protesilaos Stavrou 9401dbaa30
emacs: add display-buffer-alist entry for Org agenda views popup 2025-01-17 12:26:38 +02:00
Protesilaos Stavrou 11d8cadb8e
emacs: tweak prot-vertico-private-exit 2025-01-17 12:26:25 +02:00
Protesilaos Stavrou 1cce624f9f
emacs: use 'minibuffer-complete-and-exit' in 'prot-vertico-private-exit'
This way we show the candidates only if the exit does not happen.
2025-01-12 09:45:28 +02:00
Protesilaos Stavrou bbfc92aae5
emacs: tweak prot-vertico-private-exit for when RET has multiple matches 2025-01-12 08:22:04 +02:00
Protesilaos Stavrou 43fb87bd47
emacs: tweak the display-buffer-alist for VC pull/push output 2025-01-12 08:11:33 +02:00
Protesilaos Stavrou 5b2d92ca21
herbstluft: align tab titles to the centre 2025-01-12 08:10:51 +02:00
Protesilaos Stavrou fd5c7a7844
emacs: make prot-modeline--string-truncate-p more robust 2025-01-10 08:00:32 +02:00
Protesilaos Stavrou 48426f9f5a
emacs: refine prot-vertico-private-exit 2025-01-08 13:01:53 +02:00
Protesilaos Stavrou d584977a10
emacs: define what RET does with "private" Vertico 2025-01-08 11:17:12 +02:00
Protesilaos Stavrou b1010f561c
emacs: update the fontaine configuration
This is in light of the latest developments:
<https://protesilaos.com/codelog/2025-01-08-emacs-fontaine-custom-theme/>.
2025-01-08 11:16:21 +02:00
Protesilaos Stavrou 7fe8da9143
rofi: increase the width and height a little bit
This is still decent on the desktop and looks fine on the laptop too,
whereas before the laptop's view was too small to show the candidates
properly.
2025-01-08 11:15:22 +02:00
Protesilaos Stavrou 6bab223970
emacs: tweak how the "next" and "complete" private vertico to move to the first candidate
I believe this was working before, but maybe my latest change affected
it. Anyway, this is more explicit.
2025-01-07 09:41:38 +02:00
Protesilaos Stavrou b5f4a45154
emacs: tweak how the prompt is selected in my "minimal" Vertico view 2025-01-07 09:28:12 +02:00
Protesilaos Stavrou 5529527847
emacs: define the vertico-multiform-comamnds as well
This is to exempt these commands from whatever I specify in the
vertico-multiform-categories.
2025-01-07 09:27:17 +02:00
Protesilaos Stavrou e077c00d65
emacs: update the configuration of spacious-padding-subtle-mode-line
This uses the current development snapshot of spacious-padding which
defines those two new faces.
2025-01-07 08:37:27 +02:00
Protesilaos Stavrou bb3db6d697
Merge branch 'master' of github.com:protesilaos/dotfiles 2024-12-28 09:55:23 +02:00
Protesilaos Stavrou 43eec5dbbd
emacs: make notmuch message completion forcibly use notmuch functions 2024-12-28 09:55:10 +02:00
Protesilaos Stavrou 13c7d7fd8d
notmuch: rewrite the pre-new tag 2024-12-28 09:53:52 +02:00
Protesilaos Stavrou d21bf0a50f
xorg-twm: put the compose key on menu, not insert 2024-12-28 08:04:54 +02:00
Protesilaos Stavrou a69429b7ab
herbstluft: use a single tabbed frame on the smaller screen
This is for when I am on the laptop, where the side-by-side tiling
scheme is not optimal for any application other than Emacs and the terminal.
2024-12-27 11:21:01 +02:00
Protesilaos Stavrou d0a7cf9430
emacs: configure 'trusted-content' user option 2024-12-25 19:20:17 +02:00
Protesilaos Stavrou b179265c36
herbstluft: define a FULL workspace layout for herbstluftwm_load_workspace_layout 2024-12-24 08:39:49 +02:00
Protesilaos Stavrou d1d7099d3e
herbstluftwm: reword description in the README 2024-12-19 09:39:41 +02:00
Protesilaos Stavrou cca47f009b
herbstluft: load preset workspace layouts if the display is wide 2024-12-19 09:34:17 +02:00
Protesilaos Stavrou 71687c6687
emacs: make some packages load only in a graphical session 2024-12-18 09:58:34 +02:00
Protesilaos Stavrou 332e9a0438
emacs: load browse-url after 1 second of idleness 2024-12-18 09:55:53 +02:00
Protesilaos Stavrou 8a563f8be2
emacs: update standard-themes configuration 2024-12-17 12:15:54 +02:00
Protesilaos Stavrou f8c3b59ba5
xtwm: update cursor theme 2024-12-17 12:15:19 +02:00
Protesilaos Stavrou 2358935727
emacs: tweak a prot-org-clock--template-no-effort parameter 2024-12-15 16:30:51 +02:00
Protesilaos Stavrou 73eee7cc30
mbsync: check more frequently for new emails 2024-12-15 10:26:23 +02:00
Protesilaos Stavrou 82adc69468
emacs: regenerate prot-simple.el 2024-12-15 10:26:07 +02:00
Protesilaos Stavrou a6de44b5f5
EXPERIMENTAL emacs: define custom clocking commands in prot-org 2024-12-15 10:25:44 +02:00
Protesilaos Stavrou c266936beb
emacs: add missing doc string in prot-org 2024-12-15 10:22:25 +02:00
Protesilaos Stavrou 5986063b6a
emacs: rename prot-org function to select file and heading 2024-12-15 10:22:25 +02:00
Protesilaos Stavrou 5bf40d88b8
emacs: make a couple of wrapper command declare what they are 2024-12-14 11:36:19 +02:00
Protesilaos Stavrou bd90713a68
emacs: remove addition to save-some-buffers-action-alist
This is already supported by default, but I had not seen it.
2024-12-14 11:35:49 +02:00
Protesilaos Stavrou 1389e9d7a0
emacs: tangle latest commit 2024-12-11 13:11:14 +02:00
Protesilaos Stavrou e39e51d8a6
emacs: make save-some-buffers show diff on demand 2024-12-11 13:10:35 +02:00
Protesilaos Stavrou 6ae140c912
herbstluft: add commands to load desired workspace layout 2024-12-11 11:36:28 +02:00
Protesilaos Stavrou f5dbc1490f
herbstluft: load desired workspace layouts at startup 2024-12-08 09:29:32 +02:00
Protesilaos Stavrou 835393d4e8
sxhkdrc: revise the herbstluft key bindings 2024-12-07 12:05:53 +02:00
Protesilaos Stavrou 2ad651089e
Remove reference to "Arch Linux" in the README and keep it generic 2024-12-06 12:41:13 +02:00
Protesilaos Stavrou 4ee7a32a00
gkt: update default toolkit and icon themes 2024-12-06 12:40:06 +02:00
Protesilaos Stavrou 816fa79f7d
delight: fix bug in _xsettingsd to match themes with numbers in their name 2024-12-06 12:39:28 +02:00
Protesilaos Stavrou 62eb3926de
bashrc: define aliases for apt 2024-12-06 12:37:03 +02:00
Protesilaos Stavrou 6f4bc433f6
Xresources: disable xterm cursor blink 2024-12-06 08:52:57 +02:00
Protesilaos Stavrou e2442777f9
sxhkdrc: notify-send when reloading Xresources 2024-12-06 08:49:11 +02:00
Protesilaos Stavrou edd56c05fc
Xresources: tweak font heights 2024-12-06 08:49:01 +02:00
Protesilaos Stavrou 3551bc3f72
xtwm_common: simplify setxkbmap
I do not need more with my custom keyboard.
2024-12-06 08:42:55 +02:00
Protesilaos Stavrou abef0172d7
BREAKING fix some shell script names caused by commit e94e0e2b 2024-12-06 08:41:46 +02:00
Protesilaos Stavrou 0ec1827548
Make herbstluft and bspwm configs set envvars because ~/.profile does not 2024-12-06 08:29:11 +02:00
Protesilaos Stavrou aff2556f9b
herbstluft: disable virtual monitors 2024-12-06 08:27:24 +02:00
Protesilaos Stavrou e94e0e2ba0
BREAKING rename all shell scripts to .sh and put them in ~/.local/bin 2024-12-06 08:25:09 +02:00
Protesilaos Stavrou eb7128423a
polybar: use config.ini instead of config 2024-12-06 08:04:02 +02:00
Protesilaos Stavrou 119dae42e3
notmuch: add a pre-new file to remove unwanted mail from the server 2024-12-01 12:39:39 +02:00
Protesilaos Stavrou 71a419e54d
notmuch: disable some of the post-new hooks I do not rely on anymore 2024-12-01 12:39:21 +02:00
Protesilaos Stavrou 4c9cda0cb3
notmuch: do not exclude any tag from the searches 2024-12-01 12:39:01 +02:00
Protesilaos Stavrou aeb48c365a
REVISE the email setup
I tried mailbox.org for a while, but kept encountering issues. Some of
my messages would not be delivered at all. Messages from others would
not reach me either. There was not discernible pattern on when
something would fail, which made everything feel wrong.

I am back to using Gandi for at least another year. Will then review
my options.
2024-12-01 12:36:56 +02:00
Protesilaos Stavrou d78cafdd37
emacs: tweak the spacing of some org-capture templates 2024-12-01 09:02:36 +02:00
Protesilaos Stavrou 1ceb88c2c9
emacs: define prot-org-goto-heading-in-file alias for prot-org-capture-select-project 2024-11-27 08:00:26 +02:00
Protesilaos Stavrou e40555df96
emacs: make prot-org-file-prompt use the correct category 2024-11-27 07:59:40 +02:00
Protesilaos Stavrou e731d32021
emacs: replace if-let with if-let* to placate the compiler 2024-11-25 10:18:46 +02:00
Protesilaos Stavrou 41e74998e1
emacs: define prot-org-get-dotemacs-link command 2024-11-25 09:42:32 +02:00
Protesilaos Stavrou 966d814912
emacs: add prefix to all org-capture CUSTOM_ID to work with HTML 2024-11-25 09:31:16 +02:00
Protesilaos Stavrou 585c17d35e
emacs: add CUSTOM_ID to all org-capture templates 2024-11-25 09:29:39 +02:00
Protesilaos Stavrou 7b4320ba25
emacs: tweak the org-capture template that selects a heading 2024-11-25 09:25:26 +02:00
Protesilaos Stavrou 802231990c
emacs: refine the org-capture templates 2024-11-24 17:17:44 +02:00
Protesilaos Stavrou f301b46d60
emacs: add org-capture template to select file and project 2024-11-24 17:08:13 +02:00
Protesilaos Stavrou dc99ff49fd
emacs: add work-progress-code to select a project via org-capture 2024-11-24 16:35:14 +02:00
Protesilaos Stavrou 514515179b
emacs: tweak notmuch saved searches 2024-11-21 15:11:36 +02:00
Protesilaos Stavrou c17dd09d06
Update email settings to include new address (also see 27b3d81b, 5fafcd7b) 2024-11-21 10:36:32 +02:00
Protesilaos Stavrou 5fafcd7b6f
emacs: remove some now-discontinued notmuch filters 2024-11-20 10:54:17 +02:00
Protesilaos Stavrou 27b3d81b9b
BREAKING: revise the email setup for the accounts in use 2024-11-20 08:27:11 +02:00
Protesilaos Stavrou a903f5df5e
notmuch: remove some long-outdated entries from the post-new hook 2024-11-20 08:13:55 +02:00
Protesilaos Stavrou d3ed5912bd
emacs: test default value of dired-preview-display-action-alist 2024-11-20 08:13:05 +02:00
Protesilaos Stavrou da0838e0af
emacs: make two small tweaks to the main Org file 2024-11-20 08:12:08 +02:00
Protesilaos Stavrou e9b056ec8b
emacs: remove testing code from Denote configuration 2024-11-20 08:11:48 +02:00
Protesilaos Stavrou 4890f3786e
emacs: define some easier key bindings for common actions 2024-11-20 08:08:52 +02:00
Protesilaos Stavrou 76506813a6
emacs: define prot-simple-kill-ring-save and bind it to M-w
I never use M-w without a region, so this is basically an easier key
binding for 'prot-simple-copy-line'.
2024-11-20 08:08:52 +02:00
Protesilaos Stavrou 36073bca2d
emacs: set default value in Org capture prompt for time-sensitive task 2024-11-17 12:06:00 +02:00
Protesilaos Stavrou db6b40130e
emacs: define prot-shell command 2024-11-17 12:05:44 +02:00
Protesilaos Stavrou cf3fd7d82f
emacs: tweak the prot-org capture templates 2024-11-10 07:54:26 +02:00
Protesilaos Stavrou 91f51b9179
emacs: tweak the org-capture-templates 2024-11-10 07:54:09 +02:00
Protesilaos Stavrou e980f93c62
Revert "Update email setup to include a gmail account (FOR TESTING)"
This reverts commit 4503ebfedd.
2024-11-08 21:41:47 +02:00
Protesilaos Stavrou 86d0ad731c
emacs: fix prot-modeline-narrow docstring
Thanks to Tomasz Hołubowicz for informing me about this in issue 61 on
the GitLab mirror: <https://gitlab.com/protesilaos/dotfiles/-/issues/61>.
2024-11-04 07:38:38 +02:00
Protesilaos Stavrou 4503ebfedd
Update email setup to include a gmail account (FOR TESTING)
I need to see how this setup behaves with a gmail account, so I can
help people who ask me about it during my coaching sessions.
2024-11-04 07:37:39 +02:00
Protesilaos Stavrou 5831fb8b1c
emacs: fix an error in some custom Denote code 2024-11-04 07:36:20 +02:00
Protesilaos Stavrou 5647814e63
emacs: update ef-themes key bindings 2024-10-31 08:12:52 +02:00
Protesilaos Stavrou ec2ed7fa1a
emacs: make sure custom agenda shows important tasks without a date properly 2024-10-31 08:12:15 +02:00
Protesilaos Stavrou deb95f9f01
emacs: include binding for vc-clone (Emacs 31) 2024-10-31 08:11:51 +02:00
Protesilaos Stavrou d051c2af48
emacs: make tweaks to the Denote configuration 2024-10-31 08:11:36 +02:00
Protesilaos Stavrou 716ee9d600
emacs: tweak ef themes configuration to include ef-themes-rotate 2024-10-25 13:48:20 +03:00
Protesilaos Stavrou c8b458e0b6
emacs: tweak Modus themes configuration to include modus-themes-rotate 2024-10-25 13:47:56 +03:00
Protesilaos Stavrou 2ccee151e2
Use if-let* and friends because the others are deprecated in Emacs 31 2024-10-25 13:47:37 +03:00
Protesilaos Stavrou 671eb82136
emacs: make minor change to initial-scratch-message 2024-10-24 11:39:44 +03:00
Protesilaos Stavrou 0e191e8cf7
emacs: tweak pulsar and lin faces 2024-10-20 12:59:15 +03:00
Protesilaos Stavrou 1db8fc903e
emacs: add prot-dired-search-flat-list command 2024-10-19 10:58:43 +03:00
Protesilaos Stavrou 6eccd5edb0
emacs: revise the "small" preset of fontaine 2024-10-11 08:40:33 +03:00
Protesilaos Stavrou 171e13ae33
emacs: fix a typo 2024-10-11 08:40:25 +03:00
Protesilaos Stavrou c62fa97050
emacs: add "déjà" to abbrev-mode 2024-10-11 08:40:05 +03:00
Protesilaos Stavrou 8b76d602d7
emacs: add delete-pair-push-mark for Emacs 31 2024-09-25 11:52:46 +03:00
Protesilaos Stavrou d5a1ad4b12
emacs: define code to run a command in a popup frame 2024-09-19 13:25:50 +03:00
Protesilaos Stavrou 66a5a7c432
emacs: tweak the scratch buffer message 2024-09-19 11:10:59 +03:00
Protesilaos Stavrou 479cba632c
emacs: incorporate the Emacs 31 version of editable grep 2024-09-19 11:06:25 +03:00
Protesilaos Stavrou c8baaa79a2
Update all the copyright years 2024-09-19 10:34:14 +03:00
Protesilaos Stavrou 724b24c19d
emacs: make small change to the cursory-presets 2024-09-15 09:39:19 +03:00
Protesilaos Stavrou bbfdac8e6c
emacs: tweak the initial-scratch-message 2024-09-15 09:39:06 +03:00
Protesilaos Stavrou 734626eece
mbsyncrc: make the far side the source of truth 2024-09-11 10:34:54 +03:00
Protesilaos Stavrou f8eadc740b
emacs: add my show-font package 2024-09-11 10:34:44 +03:00
Protesilaos Stavrou 27d883695d
emacs: tweak the settings for the scratch buffer 2024-09-11 10:32:41 +03:00
Protesilaos Stavrou ec079866b3
emacs: update fontaine 'small' preset 2024-09-07 13:15:19 +03:00
Protesilaos Stavrou b7dd5ac37b
emacs: disable some keybindings I never use and do not want by accident 2024-09-07 13:14:57 +03:00
Protesilaos Stavrou 1bcfe0b5f8
emacs: change lin colour for some variety 2024-09-07 13:14:49 +03:00
Protesilaos Stavrou 3577faa758
emacs: change pulsar colours for some variety 2024-09-07 13:14:05 +03:00
Protesilaos Stavrou 5dd0351bdc
emacs: tweak denote-rename-buffer-format spacing 2024-09-07 13:13:34 +03:00
Protesilaos Stavrou 174e2d0049
emacs: set up the magit-list-repositories 2024-09-03 09:56:42 +03:00
Protesilaos Stavrou 1c30a30359
emacs: make the transient not pop up eagerly but wait a bit 2024-09-03 09:56:21 +03:00
Protesilaos Stavrou c2879109bf
emacs: tweak how the nerd-icons-completion is loaded 2024-09-03 09:55:34 +03:00
Protesilaos Stavrou 655a7f70c8
emacs: use new symbol for Org fold catching invisible edits
The other one is deprecated.
2024-09-03 09:55:20 +03:00
Protesilaos Stavrou d5b405b847
emacs: fix a strange bug with the macroexpansion of use-package for marginalia 2024-09-03 09:54:28 +03:00
Protesilaos Stavrou 124477a75f
emacs: simplify the ready-player configuration 2024-09-03 09:54:10 +03:00
Protesilaos Stavrou 6dc22ce935
emacs: update fontaine configuration 2024-09-03 09:52:03 +03:00
Protesilaos Stavrou 2a1c424c55
bashrc: tweak when the PAGER changes in Emacs 2024-09-03 09:50:23 +03:00
Protesilaos Stavrou fe39a692ff
emacs: make partial-completion behave like substring (Emacs 31) 2024-08-27 18:17:29 +03:00
Protesilaos Stavrou b3fe464587
emacs: make elfeed use visual-line-mode 2024-08-27 11:05:43 +03:00
Protesilaos Stavrou 01693f870b
emacs: tweak how consult-denote is loaded 2024-08-26 12:38:34 +03:00
Protesilaos Stavrou 85f4591312
emacs: set new shr-fill-text to nil 2024-08-26 12:37:15 +03:00
Protesilaos Stavrou 9caf7a5572
emacs: include Toronto in the world-clock 2024-08-26 12:36:56 +03:00
Protesilaos Stavrou 6570b359d3
emacs: include the new ef-eagle and ef-owl themes 2024-08-19 15:13:39 +03:00
Protesilaos Stavrou bd3c07f6d8
emacs: use F2 to toggle input method 2024-08-19 15:13:00 +03:00
Protesilaos Stavrou 20aef2616b
emacs: prevent ready-player from autoplaying (interferes with dired-preview) 2024-08-15 10:38:12 +03:00
Protesilaos Stavrou 8e7ee34266
emacs: make small tweak to the style of the Org agenda Now indicator 2024-08-13 10:44:40 +03:00
Protesilaos Stavrou 9c59b10527
emacs: add comment about the use of 'cd' 2024-08-13 10:44:26 +03:00
Protesilaos Stavrou 000c3ee8bd
emacs: document the deprecated email modules (mu4e, gnus) 2024-08-13 10:44:01 +03:00
Protesilaos Stavrou 66f18b9b14
emacs: tweak the notmuch-indicator section 2024-08-06 09:31:13 +03:00
Protesilaos Stavrou 7645309311
emacs: document the ol-notmuch section 2024-08-06 09:30:34 +03:00
Protesilaos Stavrou ef622e2079
emacs: document the prot-notmuch.el configuration 2024-08-06 09:27:14 +03:00
Protesilaos Stavrou c6c2004e6c
emacs: document notmuch hooks and keys section 2024-08-06 09:21:45 +03:00
Protesilaos Stavrou 435e1b48b9
emacs: update the commentary in the logos section 2024-08-06 09:15:35 +03:00
Protesilaos Stavrou 678bceccd9
emacs: reference the correct symbol in the breadcrumb :functions 2024-08-06 09:15:12 +03:00
Protesilaos Stavrou ce7a63349d
emacs: update dired-preview config to match latest developments 2024-08-06 09:14:42 +03:00
Protesilaos Stavrou 6f0ed456c9
emacs: update dired-preview in light of latest developments 2024-08-05 09:04:09 +03:00
Protesilaos Stavrou 0494495eec
emacs: fix a trailing escape in a regexp for dired-preview 2024-08-05 09:02:47 +03:00
Protesilaos Stavrou 881f15cf24
emacs: truncate lines also in dired-mode 2024-08-05 09:02:19 +03:00
Protesilaos Stavrou df82599909
emacs: enable breadcrumb-mode only when relevant 2024-07-29 12:22:47 +03:00
Protesilaos Stavrou 376dbe1ab3
emacs: update the ready-player configuration 2024-07-29 12:22:41 +03:00
Protesilaos Stavrou 2fb1e9b1ef
emacs: add prot/dired-preview-open command 2024-07-29 12:22:41 +03:00
Protesilaos Stavrou 942da3bc43
emacs: update the Denote configuration 2024-07-28 14:15:40 +03:00
Protesilaos Stavrou 9885f47c0f
emacs: add ready-player package 2024-07-28 14:14:42 +03:00
Protesilaos Stavrou 19bae8c29d
emacs: tweak the value of dired-preview-ignored-extensions-regexp 2024-07-28 14:14:21 +03:00
Protesilaos Stavrou 62fc1bb79a
emacs: make some minor corrections 2024-07-28 14:06:53 +03:00
Protesilaos Stavrou 6f75d07613
emacs: add another fontaine preset 2024-07-24 14:25:14 +03:00
Protesilaos Stavrou 4e323adfb6
emacs: tweak spacious-padding styles 2024-07-24 14:24:57 +03:00
Protesilaos Stavrou 04da3314d2
emacs: set consult-project-function to nil 2024-07-23 09:53:44 +03:00
Protesilaos Stavrou 45c2724a75
delight: quote value of current theme for GNOME
Otherwise the equality test fails.
2024-07-19 16:21:09 +03:00
Protesilaos Stavrou 355a9ae2b7
emacs: include csv-mode package 2024-07-16 14:29:50 +03:00
Protesilaos Stavrou 8355cba6ff
emacs: add missing properties drawer in prot-emacs.org 2024-07-16 14:29:32 +03:00
Protesilaos Stavrou a91b75c3de
emacs: note that which-key is part of Emacs 30 2024-07-11 21:12:04 +03:00
Protesilaos Stavrou 171eedd8b5
emacs: fix nerd-icons-completion setup for marginalia 2024-07-06 17:21:55 +03:00
Protesilaos Stavrou f539152039
emacs: add missing parenthesis to consult-denote package 2024-07-06 17:13:49 +03:00
Protesilaos Stavrou 532c302335
emacs: add commentary on the Org agenda 2024-07-06 13:48:36 +03:00
Protesilaos Stavrou 52745c58c0
emacs: tweak how nerd-icons-completion-mode is enabled 2024-07-06 13:48:18 +03:00
Protesilaos Stavrou 0c8befaef0
emacs: tweak the Denote setup 2024-07-06 13:47:38 +03:00
Protesilaos Stavrou 87c0c40234
emacs: disable the prot-shell conversion to invidious
Most of those do not work, plus they may be sketchy domains.
2024-06-30 17:56:02 +03:00
Protesilaos Stavrou 2503c017ab
emacs: update the configuration of Denote 2024-06-30 17:55:51 +03:00
Protesilaos Stavrou 180314deff
emacs: remove obsolete hook for eww bookmark handler
This feature is built into Emacs 28.
2024-06-26 19:35:34 +03:00
Protesilaos Stavrou 4e07f48f8a
emacs: remove EWW bookmark.el handler (is in Emacs 28) 2024-06-22 17:00:39 +03:00
Protesilaos Stavrou e3f649ffc4
emacs: refine EWW implementation of bookmark.el handler 2024-06-22 16:52:54 +03:00
Protesilaos Stavrou 3a6bae411b
emacs: fix a typo in a doc string 2024-06-22 13:57:17 +03:00
Protesilaos Stavrou c1fef3fa07
emacs: tweak how dired-preview is started 2024-06-22 13:57:15 +03:00
Protesilaos Stavrou 8609df051f
emacs: lazy load the prot-coach.el 2024-06-22 13:57:15 +03:00
Protesilaos Stavrou 725b525956
emacs: make some minor tweaks to the Denote configuration 2024-06-22 13:57:15 +03:00
Protesilaos Stavrou af928c00f8
emacs: define a toggle for auto fill and visual lines 2024-06-22 13:57:15 +03:00
Protesilaos Stavrou 438f9549c1
emacs: provide eww and bookmark.el integration 2024-06-22 13:44:41 +03:00
Protesilaos Stavrou 2fd4e7e57d
emacs: tweak modus-themes testing setup 2024-06-11 11:25:22 +03:00
Protesilaos Stavrou ce9c480a07
emacs: add mct key for deleting directories in one go 2024-06-11 11:22:41 +03:00
Protesilaos Stavrou ea8d799eae
emacs: update the section about org-capture 2024-06-11 10:02:35 +03:00
Protesilaos Stavrou 6af9cd65a6
msmtp: fix typo 2024-05-23 07:18:50 +03:00
Protesilaos Stavrou 2f1b4241ea
emacs: refine the substitute package 2024-05-23 07:18:31 +03:00
Protesilaos Stavrou a1bf0516e5
Sync state of prot-emacs-notmuch.el 2024-05-21 07:33:57 +03:00
Protesilaos Stavrou bcb02f95ac
emacs: update substitute configuration 2024-05-21 07:32:50 +03:00
Protesilaos Stavrou eec248e5fb
msmtp: enable starttls 2024-05-21 07:32:16 +03:00
Protesilaos Stavrou 2dd8933e92
emacs: update the section about the software and hardware I use 2024-05-19 10:40:14 +03:00
Protesilaos Stavrou 59ea28dbff
emacs: make notmuch-indicator queries also check inbox tag
This is to ensure that messages are not counter from places I do not
want to see.
2024-05-18 21:06:26 +03:00
Protesilaos Stavrou 7ec2229ab5
emacs: fix bug with the value given to spacious-padding for subtle inactive modeline 2024-05-17 14:05:56 +03:00
Protesilaos Stavrou a12daf3827
emacs: tweak the modus-themes override (for testing purposes) 2024-05-16 10:38:32 +03:00
Protesilaos Stavrou 0e3a9615c3
emacs: make sure the spacious-padding mode line is set 2024-05-16 10:34:23 +03:00
Protesilaos Stavrou f13d3fab5d
emacs: tweak modus-themes-common-palette-overrides (for testing purposes) 2024-05-16 07:41:20 +03:00
Protesilaos Stavrou fe1fb5acf7
emacs: tweak the Cursory presets 2024-05-16 07:38:41 +03:00
Protesilaos Stavrou cddc1cd258
emacs: re-enable the spacious-padding-subtle-mode-line (for testing purposes) 2024-05-16 07:27:23 +03:00
Protesilaos Stavrou fc4cf67f1d
emacs: tweak how nerd-icons-completion is set up 2024-05-16 07:19:29 +03:00
Protesilaos Stavrou 5556d3409b
emacs: use right click for flyspell correction 2024-05-16 07:19:04 +03:00
Protesilaos Stavrou e2361151b9
emacs: add recentf section 2024-05-13 14:03:33 +03:00
Protesilaos Stavrou ff0361d73e
emacs: update the notmuch-indicator configuration 2024-05-13 13:58:07 +03:00
Protesilaos Stavrou e13f39df86
emacs: tweak the configuration of mandoura 2024-05-13 13:57:33 +03:00
Protesilaos Stavrou 83aedb8e6a
emacs: tweak the default frame width 2024-05-13 13:57:02 +03:00
Protesilaos Stavrou e0bc07c602
emacs: fix dired-subtree loading 2024-05-02 11:02:40 +03:00
Protesilaos Stavrou 48439f4500
emacs: fix theme-buffet arrangement for the modus-themes 2024-04-29 17:28:35 +03:00
Protesilaos Stavrou b1ee965bda
emacs: tweak how theme-buffet is set up 2024-04-29 11:48:09 +03:00
Protesilaos Stavrou caf3c13dc9
emacs: add binding for restart-emacs 2024-04-29 11:47:55 +03:00
Protesilaos Stavrou 082ec42981
emacs: tweak dired-preview settings and hook 2024-04-29 11:21:02 +03:00
Protesilaos Stavrou 133b4e8582
emacs: tweak mct-completion-passlist 2024-04-29 11:20:38 +03:00
Protesilaos Stavrou 036515a2e1
emacs: tweak how fontaine is started 2024-04-29 11:19:58 +03:00
Protesilaos Stavrou 266dd32b16
emacs: add two new ef-themes to theme-buffet list 2024-04-29 11:19:28 +03:00
Protesilaos Stavrou adbe5673f5
emacs: remove needless 'require' for package.el 2024-04-27 08:38:59 +03:00
Protesilaos Stavrou 5c215930b2
emacs: remove shadowing of M-s M-s key 2024-04-27 08:31:37 +03:00
Protesilaos Stavrou 227f5b65f6
Remove outdate references to SourceHut; use GitHub source
Thanks to doolio for pointing this out in issue 6:
<https://github.com/protesilaos/dotfiles/issues/6>.
2024-04-26 13:22:05 +03:00
Protesilaos Stavrou 6c7d0e0bdd
emacs: reorganise some Org settings 2024-04-25 20:58:19 +03:00
Protesilaos Stavrou a08851e959
emacs: fix a pending TODO 2024-04-25 20:41:12 +03:00
Protesilaos Stavrou 0a7d809228
emacs: document section about Org export 2024-04-25 20:39:46 +03:00
Protesilaos Stavrou 18a5508790
emacs: document Org section about links 2024-04-25 20:38:59 +03:00
Protesilaos Stavrou 06a53d5845
emacs: remove outdated TODO keyword from heading 2024-04-25 18:05:19 +03:00
Protesilaos Stavrou 95b15612f9
emacs: document Org time/state logging 2024-04-25 16:48:12 +03:00
Protesilaos Stavrou e96df5d82f
emacs: document section about Org tags 2024-04-25 16:36:25 +03:00
Protesilaos Stavrou ee0f01ca38
emacs: document section about Org todo and refile 2024-04-25 16:22:34 +03:00
Protesilaos Stavrou 07a5d15086
emacs: expand key bindings for dired-subtree 2024-04-25 16:10:32 +03:00
Protesilaos Stavrou e1ee0b2407
emacs: defer theme-buffet to not slow down startup 2024-04-25 08:41:50 +03:00
Protesilaos Stavrou 4d4e82fc63
Reword the README to be up to date 2024-04-21 07:06:50 +03:00
Protesilaos Stavrou 88ae28d5ea
emacs: fix how agitate is set up 2024-04-20 21:56:49 +03:00
Protesilaos Stavrou 24eecba5c8
bashrc: update how Emacs shell gets its PAGER 2024-04-20 21:51:01 +03:00
175 changed files with 15749 additions and 10431 deletions

1
.gitignore vendored
View file

@ -29,3 +29,4 @@
user-emacs.*
prot-emacs-pre-custom.*
prot-emacs-post-custom.*
*-autoloads.el

View file

@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Dotfile collection with post-install customisations for GNU/Linux
Copyright (C) 2019 Protesilaos Stavrou
Copyright (C) 2019-2026 Protesilaos
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
Protesilaos Stavrou's Dotfiles Copyright (C) 2019 Protesilaos Stavrou
Protesilaos's Dotfiles Copyright (C) 2019-2026 Protesilaos
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.

View file

@ -1,5 +1,5 @@
[user]
name = Protesilaos Stavrou
name = Protesilaos
email = info@protesilaos.com
signingkey = C3C6BC9BBE2F62C7C586EBAA99BD6459CD5CA3EA
[core]

View file

@ -1,4 +1,4 @@
# My Emacs and other configurations for Arch Linux
# My Emacs and other configurations for Linux
This is the set of files that powers my day-to-day computing experience.
GNU Emacs is its centrepiece, with other programs providing ancillary
@ -26,12 +26,10 @@ or contribute any fixes, if you will.
## Emacs setup
I do not recommend you reproduce my Emacs setup because I do not use
the de facto standard of `use-package` to configure packages. I
prefer a simpler approach.
If you insist though, the files are in the `emacs` directory. Add them
to your home directory with:
I do not recommend you reproduce my Emacs setup because I do not
design it as a distribution for other people. If you insist though,
the files are in the `emacs` directory. Add them to your home
directory with:
```sh
/path/to/prot-dotfiles $ stow -t "$HOME" emacs
@ -58,11 +56,11 @@ run stow again with the `-R` flag:
## Window managers
My tiling window managers are bspwm, herbstluftwm, and i3 (in no
particular order). They are all configured to be almost the same:
My tiling window managers for Xorg are bspwm, herbstluftwm, and i3 (in
no particular order). They are all configured to be almost the same:
they share settings for the wallpaper, system panel, theme, keyboard
layout, and display compositor. All settings are in the `xorg-twm`
directory ("twm" stands for "tiling window manager"). What
layout, and display compositor. All settings are in the `xorg-twm`
directory ("twm" stands for "tiling window manager"). What
differentiates the window managers is their individual features.
+ **bspwm:** I have been using it for years and consider it top-notch.
@ -72,25 +70,26 @@ differentiates the window managers is their individual features.
them.
+ **herbstluftwm (hlwm):** herbstluftwm prioritises manual tiling
methods and can treat arbitrary rectangles of a monitor as virtual
monitors. The virtual monitors feature is perfect for anyone with a
widescreen display. The one I have (which is not mine, but anyway)
is 2560x1080, so I split it by default into a regular 1920x1080 area
and another "sidebar" of 640x1080. herbstluftwm has the concept of
"frame", which is a container of regular windows. Each frame can
have its own layout, including a vertical/horizontal stack, grid,
and tabbed.
methods. It has the concept of "frames", which are rectangles that
include regular app windows. Each frame has its own dimensions are
layout. For example, it is possible to have a frame where windows
are vertically layed out one below the other and another frame with
a tabbed layout. What makes this even more appealing, is the ease
with which a workspace can be preconfigured (or dymanically
adjusted) to a given arrangement of frames and their layouts. I
think herbstluftwm is optimal for screens wider than the the regular
1920x1080. Otherwise, the manual tiling method adds cognitive load
that the user does not have while using bspwm.
+ **i3 (or i3wm):** The first tiling window manager I ever used (circa
2017). The reason I abandoned it back in the day in favour of bspwm
2017). The reason I abandoned it back in the day in favour of bspwm
is because its default tiling method requires manual intervention to
change the split direction. On a small laptop monitor, I prefer
this to be done automatically, hence bspwm. Though on a wide
monitor, I typically keep the split direction constant. i3 has the
concept of the "container" (same as herbstluftwm "frame"), which can
be set to stacked, tabbed, or tiled layouts. As such, i3 is
somewhere between bspwm and herbstluftwm. Choose according to your
needs.
change the split direction. On a small laptop monitor, I prefer this
to be done automatically, hence bspwm. Though on a wide monitor, I
typically keep the split direction constant. i3 has the concept of
the "container" (same as the herbstluftwm "frame"), which can be set
to stacked, tabbed, or tiled layouts. As such, i3 is somewhere
between bspwm and herbstluftwm. Choose according to your needs.
Check the `xtwm-key-binding-cheatsheet.md` file for an overview of
their key bindings.
@ -100,9 +99,13 @@ always use herbstluftwm because of its ability to support virtual
monitors. Otherwise I oscillate between bspwm and i3, depending on
the machine I am using.
What about Wayland? It is not ready yet: I am missing something like
sxhkd and certain applications do not work properly on it. I may
check again in a few years.
What about Wayland? I have experimented with it on GNOME where it
works fine. I even have some configurations for tiling compositors,
but am not happy with those. The Wayland ecosystem is not ready yet
for my use-case: I am missing something like sxhkd and certain
applications do not work properly on it, such screen sharing. I will
check again some time over the medium term, as Wayland seems
inevitable.
## Copying

View file

@ -26,24 +26,31 @@
# constant width of 300
width = 300
# The maximum height of a single notification, excluding the frame.
height = 300
# The height of a single notification, excluding the frame.
# dynamic height from 0 to 300
height = (0, 300)
# constant height of 300
# height = 300
# NOTE: Dunst from version 1.11 and older don't support dynamic height
# and the given value is treated as the maximum height
# Position the notification in the top right corner
# Position the notification in the bottom right corner
origin = bottom-right
# Offset from the origin
offset = 10x50
# NOTE: Dunst from version 1.11 and older use this alternative notation
# offset = 10x50
offset = (10, 25)
# Scale factor. It is auto-detected if value is 0.
scale = 0
# Maximum number of notification (0 means no limit)
notification_limit = 0
notification_limit = 20
### Progress bar ###
# Turn on the progess bar. It appears when a progress hint is passed with
# Turn on the progress bar. It appears when a progress hint is passed with
# for example dunstify -h int:value:12
progress_bar = true
@ -60,6 +67,19 @@
# Set the maximum width for the progress bar
progress_bar_max_width = 300
# Corner radius for the progress bar. 0 disables rounded corners.
progress_bar_corner_radius = 0
# Define which corners to round when drawing the progress bar. If progress_bar_corner_radius
# is set to 0 this option will be ignored.
progress_bar_corners = all
# Corner radius for the icon image.
icon_corner_radius = 0
# Define which corners to round when drawing the icon image. If icon_corner_radius
# is set to 0 this option will be ignored.
icon_corners = all
# Show how many messages are currently hidden (because of
# notification_limit).
@ -73,7 +93,8 @@
# Draw a line of "separator_height" pixel height between two
# notifications.
# Set to 0 to disable.
separator_height = 1
# If gap_size is greater than 0, this setting will be ignored.
separator_height = 2
# Padding between text and separator.
padding = 8
@ -91,6 +112,12 @@
# Defines color of the frame around the notification window.
frame_color = "#aaaaaa"
# Size of gap to display between notifications - requires a compositor.
# If value is greater than 0, separator_height will be ignored and a border
# of size frame_width will be drawn around each notification instead.
# Click events on gaps do not currently propagate to applications below.
gap_size = 0
# Define a color for the separator.
# possible values are:
# * auto: dunst tries to find a color fitting to the background;
@ -99,7 +126,12 @@
# * anything else will be interpreted as a X color.
separator_color = frame
# Sort messages by urgency.
# Sort type.
# possible values are:
# * id: sort by id
# * urgency_ascending: sort by urgency (low then normal then critical)
# * urgency_descending: sort by urgency (critical then normal then low)
# * update: sort by update (most recent always at the top)
sort = yes
# Don't remove messages, if the user is idle (no mouse or keyboard input)
@ -107,11 +139,11 @@
# Set to 0 to disable.
# A client can set the 'transient' hint to bypass this. See the rules
# section for how to disable this if necessary
idle_threshold = 120
# idle_threshold = 120
### Text ###
font = Monospace 9.5
font = Sans 8
# The spacing between lines. If the height is smaller than the
# font height, it will get raised to the font height.
@ -125,7 +157,7 @@
# <u>underline</u>
#
# For a complete reference see
# <https://developer.gnome.org/pango/stable/pango-Markup.html>.
# <https://docs.gtk.org/Pango/pango_markup.html>.
#
# strip: This setting is provided for compatibility with some broken
# clients that send markup even though it's not enabled on the
@ -184,19 +216,28 @@
### Icons ###
# Align icons left/right/off
# Recursive icon lookup. You can set a single theme, instead of having to
# define all lookup paths.
enable_recursive_icon_lookup = true
# Set icon theme (only used for recursive icon lookup)
# icon_theme = Adwaita
# You can also set multiple icon themes, with the leftmost one being used first.
icon_theme = "Adwaita, Papirus-Dark, Papirus-Light"
# Align icons left/right/top/off
icon_position = left
# Scale small icons up to this size, set to 0 to disable. Helpful
# for e.g. small files or high-dpi screens. In case of conflict,
# max_icon_size takes precedence over this.
min_icon_size = 0
min_icon_size = 32
# Scale larger icons down to this size, set to 0 to disable
max_icon_size = 32
max_icon_size = 128
# Paths to default icons.
icon_path = /usr/share/icons/Adwaita/16x16/status/:/usr/share/icons/Adwaita/16x16/devices/
# Paths to default icons (only necessary when not using recursive icon lookup)
icon_path = /usr/share/icons/gnome/16x16/status/:/usr/share/icons/gnome/16x16/devices/
### History ###
@ -218,10 +259,10 @@
# Always run rule-defined scripts, even if the notification is suppressed
always_run_script = true
# Define the title of the windows spawned by dunst
# Define the title of the windows spawned by dunst (X11 only)
title = Dunst
# Define the class of the windows spawned by dunst
# Define the class of the windows spawned by dunst (X11 only)
class = Dunst
# Define the corner radius of the notification window
@ -231,6 +272,13 @@
# notification height to avoid clipping text and/or icons.
corner_radius = 0
# Define which corners to round when drawing the window. If the corner radius
# is set to 0 this option will be ignored.
#
# Comma-separated list of the corners. The accepted corner values are bottom-right,
# bottom-left, top-right, top-left, top, bottom, left, right or all.
corners = all
# Ignore the dbus closeNotification message.
# Useful to enforce the timeout set by dunst configuration. Without this
# parameter, an application may close the notification sent before the
@ -240,7 +288,7 @@
### Wayland ###
# These settings are Wayland-specific. They have no effect when using X11
# Uncomment this if you want to let notications appear under fullscreen
# Uncomment this if you want to let notifications appear under fullscreen
# applications (default: overlay)
# layer = top
@ -295,23 +343,25 @@
background = "#222222"
foreground = "#888888"
timeout = 4
# Icon for notifications with low urgency, uncomment to enable
#icon = /path/to/icon
# Icon for notifications with low urgency
default_icon = dialog-information
[urgency_normal]
background = "#333333"
foreground = "#ffffff"
timeout = 6
# Icon for notifications with normal urgency, uncomment to enable
#icon = /path/to/icon
override_pause_level = 30
# Icon for notifications with normal urgency
default_icon = dialog-information
[urgency_critical]
background = "#900000"
foreground = "#ffffff"
frame_color = "#ff0000"
timeout = 0
# Icon for notifications with critical urgency, uncomment to enable
#icon = /path/to/icon
override_pause_level = 60
# Icon for notifications with critical urgency
default_icon = dialog-warning
# Every section that isn't one of the above is interpreted as a rules to
# override settings for certain messages.
@ -336,9 +386,18 @@
# new_icon
# set_stack_tag
# set_transient
# set_category
# timeout
# urgency
# icon_position
# skip_display
# history_ignore
# action_name
# word_wrap
# ellipsize
# alignment
# hide_text
# override_pause_level
#
# Shell-like globbing will get expanded.
#
@ -346,6 +405,10 @@
# GLib based applications export their desktop-entry name. In comparison to the appname,
# the desktop-entry won't get localized.
#
# You can also allow a notification to appear even when paused. Notification will appear whenever notification's override_pause_level >= dunst's paused level.
# This can be used to set partial pause modes, where more urgent notifications get through, but less urgent stay paused. To do that, you can override the following in the rules:
# override_pause_level = X
# SCRIPTING
# You can specify a script that gets run when the rule matches by
# setting the "script" option.

View file

@ -2,7 +2,7 @@
# dunst_toggle_notify --- Notify on Dunst pause/activation
#
# Copyright (c) 2021-2023 Protesilaos Stavrou <info@protesilaos.com>
# Copyright (C) 2021-2026 Protesilaos <info@protesilaos.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by

View file

@ -1,31 +1,5 @@
(defvar prot-emacs-tiling-window-manager-regexp "bspwm\\|herbstluftwm\\|i3"
"Regular expression to tiling window managers.
See definition of `prot-emacs-with-desktop-session'.")
(defmacro prot-emacs-with-desktop-session (&rest body)
"Expand BODY if desktop session is not a tiling window manager.
See `prot-emacs-tiling-window-manager-regexp' for what
constitutes a matching tiling window manager."
(declare (indent 0))
`(when-let ((session (getenv "DESKTOP_SESSION"))
((not (string-match-p session prot-emacs-tiling-window-manager-regexp))))
,@body))
(defun prot-emacs-add-to-list (list element)
"Add to symbol of LIST the given ELEMENT.
Simplified version of `add-to-list'."
(set list (cons element (symbol-value list))))
(prot-emacs-with-desktop-session
(mapc
(lambda (var)
(prot-emacs-add-to-list var '(width . (text-pixels . 1200)))
(prot-emacs-add-to-list var '(height . (text-pixels . 900)))
(prot-emacs-add-to-list var '(scroll-bar-width . 10)))
'(default-frame-alist initial-frame-alist)))
(setq frame-resize-pixelwise t
frame-inhibit-implied-resize t
frame-inhibit-implied-resize 'force
frame-title-format '("%b")
ring-bell-function 'ignore
use-dialog-box t ; only for mouse events, which I seldom use
@ -37,13 +11,60 @@ Simplified version of `add-to-list'."
inhibit-startup-echo-area-message user-login-name ; read the docstring
inhibit-startup-buffer-menu t)
;; I do not use those graphical elements by default, but I do enable
;; them from time-to-time for testing purposes or to demonstrate
;; something. NEVER tell a beginner to disable any of these. They
;; are helpful.
(menu-bar-mode -1)
(scroll-bar-mode -1)
(tool-bar-mode -1)
(scroll-bar-mode -1)
(defvar prot-laptop-p (null (directory-empty-p "/sys/class/power_supply/"))
"When non-nil, we assume to be working on a laptop.")
(when prot-laptop-p
(add-hook 'window-size-change-functions #'frame-hide-title-bar-when-maximized))
(defvar prot-pgtk-p (string-match-p "PGTK" system-configuration-features)
"When non-nil, this is a build --with-pgtk.
PGTK is the Wayland-specific build of Emacs.")
(setq initial-frame-alist `((horizontal-scroll-bars . nil)
(menu-bar-lines . 0)
(tool-bar-lines . 0)
(vertical-scroll-bars . nil)
(scroll-bar-width . ,(if prot-pgtk-p 12 6))
(width . (text-pixels . 800))
(height . (text-pixels . 900))
,@(unless prot-pgtk-p
(list '(undecorated . t)))
(border-width . 0)
,@(when prot-laptop-p
(list '(fullscreen . maximized)))))
;; Do it again after init so that any intermediate changes are not
;; retained. Note that we cannot rely on setting this to
;; `initial-frame-alist' as that may change in the meantime. We
;; explicitly set the value to be certain of the outcome. This does
;; not inhibit other programs from modifying the list, though I would
;; consider it undesirable if they were touching these specific
;; settings.
(add-hook 'after-init-hook (lambda ()
(setq default-frame-alist `((horizontal-scroll-bars . nil)
(menu-bar-lines . 0)
(tool-bar-lines . 0)
(vertical-scroll-bars . nil)
(scroll-bar-width . (if prot-pgtk-p 12 6))
(width . (text-pixels . 800))
(height . (text-pixels . 900))
,@(unless prot-pgtk-p
(list '(undecorated . t)))
(border-width . 0)
,@(when prot-laptop-p
(list '(fullscreen . maximized)))))))
(defun prot-emacs-no-minibuffer-scroll-bar (frame)
"Remove the minibuffer scroll bars from FRAME."
(when scroll-bar-mode
(set-window-scroll-bars (minibuffer-window frame) nil nil nil nil :persistent)))
(add-hook 'after-make-frame-functions #'prot-emacs-no-minibuffer-scroll-bar)
;; Temporarily increase the garbage collection threshold. These
;; changes help shave off about half a second of startup time. The
@ -64,7 +85,7 @@ Simplified version of `add-to-list'."
(add-hook 'emacs-startup-hook
(lambda ()
(setq gc-cons-threshold (* 1000 1000 8)
(setq gc-cons-threshold (* 100 100 8)
gc-cons-percentage 0.1
file-name-handler-alist prot-emacs--file-name-handler-alist
vc-handled-backends prot-emacs--vc-handled-backends)))
@ -76,56 +97,6 @@ Simplified version of `add-to-list'."
;; packages to work with, requiring a `package-refresh-contents'.
(setq package-enable-at-startup t)
;;;; General theme code
(defun prot-emacs-theme-gsettings-dark-p ()
"Return non-nil if gsettings (GNOME) has a dark theme.
Return nil if the DESKTOP_SESSION is either bspwm or
herbstluftwm, per the configuration of my dotfiles. Also check
the `delight' shell script."
(prot-emacs-with-desktop-session
(string-match-p
"dark"
(shell-command-to-string "gsettings get org.gnome.desktop.interface color-scheme"))))
(defun prot-emacs-theme-twm-dark-p ()
"Return non-nil if my custom setup has a dark theme.
I place a file in ~/.config/prot-xtwm-active-theme which contains
a single word describing my system-wide theme. This is part of
my dotfiles. Check my `delight' shell script for more."
(when-let ((file "~/.config/prot-xtwm-active-theme")
((file-exists-p file)))
(string-match-p
"dark"
(with-temp-buffer
(insert-file-contents file)
(buffer-string)))))
(defun prot-emacs-theme-environment-dark-p ()
"Return non-nil if environment theme is dark."
(or (prot-emacs-theme-twm-dark-p)
(prot-emacs-theme-gsettings-dark-p)))
(defun prot-emacs-re-enable-frame-theme (_frame)
"Re-enable active theme, if any, upon FRAME creation.
Add this to `after-make-frame-functions' so that new frames do
not retain the generic background set by the function
`prot-emacs-avoid-initial-flash-of-light'."
(when-let ((theme (car custom-enabled-themes)))
(enable-theme theme)))
;; NOTE 2023-02-05: The reason the following works is because (i) the
;; `mode-line-format' is specified again and (ii) the
;; `prot-emacs-theme-gsettings-dark-p' will load a dark theme.
(defun prot-emacs-avoid-initial-flash-of-light ()
"Avoid flash of light when starting Emacs, if needed.
New frames are instructed to call `prot-emacs-re-enable-frame-theme'."
(when (prot-emacs-theme-environment-dark-p)
(setq mode-line-format nil)
(set-face-attribute 'default nil :background "#000000" :foreground "#ffffff")
(set-face-attribute 'mode-line nil :background "#000000" :foreground "#ffffff" :box 'unspecified)
(add-hook 'after-make-frame-functions #'prot-emacs-re-enable-frame-theme)))
(prot-emacs-avoid-initial-flash-of-light)
(setq user-lisp-directory (locate-user-emacs-file "prot-lisp/"))
(add-hook 'after-init-hook (lambda () (set-frame-name "home")))

View file

@ -10,9 +10,9 @@ directory as the init.el."
(defcustom prot-emacs-load-theme-family 'modus
"Set of themes to load.
Valid values are the symbols `ef', `modus', and `standard', which
reference the `ef-themes', `modus-themes', and `standard-themes',
respectively.
Valid values are the symbols `doric', `ef', `modus', and `standard',
which reference the `doric-themes', `ef-themes', `modus-themes', and
`standard-themes', respectively.
A nil value does not load any of the above (use Emacs without a
theme).
@ -22,6 +22,7 @@ file. If that file exists in the Emacs directory, it is loaded
before all other modules of my setup."
:group 'prot-emacs
:type '(choice :tag "Set of themes to load" :value modus
(const :tag "The `doric-themes' module" doric)
(const :tag "The `ef-themes' module" ef)
(const :tag "The `modus-themes' module" modus)
(const :tag "The `standard-themes' module" standard)
@ -43,45 +44,35 @@ before all other modules of my setup."
(defcustom prot-emacs-completion-extras t
"When non-nil load extras for minibuffer completion.
These include packages such as `consult' and `embark'."
These include the packages `marginalia', `consult', `corfu',
`orderless', and `embark'."
:group 'prot-emacs
:type 'boolean)
(defcustom prot-emacs-treesitter-extras t
"When non-nil load extras for tree-sitter integration
These include packages such as `expreg' and generally anything
that adds functionality on top of what the major mode provides."
:group 'prot-emacs
:type 'boolean)
(defcustom prot-emacs-completion-in-buffer 'corfu
"Front-end for text completion in the buffer.
When the value is either `corfu' or `company', use the package of the
given name. When the value is `completion-preview' use the built-in
`completion-preview-mode'. When the value is `corfu-completion-preview',
then use the `corfu' package and `completion-preview-mode' in tandem.
(defcustom prot-emacs-load-which-key nil
"When non-nil, display key binding hints after a short delay.
This user option must be set in the `prot-emacs-pre-custom.el'
file. If that file exists in the Emacs directory, it is loaded
before all other modules of my setup."
Use the generic completions buffer if any other value is set."
:group 'prot-emacs
:type 'boolean)
(defcustom prot-emacs-load-icons nil
"When non-nil, enable iconography in various contexts.
This installs and uses the `nerd-icons' package and its variants.
NOTE that you still need to invoke `nerd-icons-install-fonts'
manually to first get the icon files.
This user option must be set in the `prot-emacs-pre-custom.el'
file. If that file exists in the Emacs directory, it is loaded
before all other modules of my setup."
:group 'prot-emacs
:type 'boolean)
:type '(choice
(const :tag "Generic completions buffer" nil)
(const :tag "Generic completions buffer" t)
(const :tag "The `corfu' package" corfu)
(const :tag "The `company' package" company)
(const :tag "The built-in `completion-preview-mode'" completion-preview)
(const :tag "Corfu and `completion-preview-mode'" corfu-completion-preview)))
(setq make-backup-files nil)
(setq backup-inhibited nil) ; Not sure if needed, given `make-backup-files'
(setq create-lockfiles nil)
;; Make native compilation silent and prune its cache.
;; Make native compilation silent.
(when (native-comp-available-p)
(setq native-comp-async-report-warnings-errors 'silent) ; Emacs 28 with native compilation
(setq native-compile-prune-cache t)) ; Emacs 29
(setq native-comp-async-report-warnings-errors 'silent))
;; Disable the damn thing by making it disposable.
(setq custom-file (make-temp-file "emacs-custom-"))
@ -93,7 +84,7 @@ before all other modules of my setup."
(mapc
(lambda (command)
(put command 'disabled nil))
'(list-timers narrow-to-region narrow-to-page upcase-region downcase-region))
'(list-timers narrow-to-region narrow-to-page upcase-region downcase-region diff-restrict-view))
;; And disable these
(mapc
@ -101,8 +92,14 @@ before all other modules of my setup."
(put command 'disabled t))
'(eshell project-eshell overwrite-mode iconify-frame diary))
;; Always start with *scratch*
(setq initial-buffer-choice t)
(setq initial-major-mode 'lisp-interaction-mode)
(setq initial-scratch-message
(format ";; This is `%s'. Type `%s' to evaluate and print results.\n\n"
'lisp-interaction-mode
(propertize
(substitute-command-keys "\\<lisp-interaction-mode-map>\\[eval-print-last-sexp]")
'face 'help-key-binding)))
(mapc
(lambda (string)
@ -139,17 +136,30 @@ before all other modules of my setup."
'(agitate
altcaps
beframe
buffer-to-pdf
consult-denote
cursory
denote
denote-journal
denote-markdown
denote-merge
denote-org
denote-sequence
denote-silo
dired-preview
doric-themes
ef-themes
fontaine
gnome-accent-theme-switcher
institution-calendar
lin
logos
mct
modus-themes
notmuch-indicator
oxford-calendar
pulsar
show-font
spacious-padding
standard-themes
substitute
@ -160,10 +170,37 @@ before all other modules of my setup."
;; Also read: <https://protesilaos.com/codelog/2022-05-13-emacs-elpa-devel/>
(setq package-pinned-packages
`(,@(mapcar
(lambda (package)
(cons package "gnu-elpa-devel"))
prot-emacs-my-packages)))
(mapcar
(lambda (package)
(cons package "gnu-elpa-devel"))
prot-emacs-my-packages))
;; These are for Emacs 31.
(setq package-review-policy
(delq nil
(append
(mapcar
(lambda (package)
(list 'not 'package package))
prot-emacs-my-packages)
(mapcar
(lambda (archive)
(let ((archive-name (car archive)))
(unless (string= archive-name "gnu-elpa-devel")
(cons 'archive archive-name))))
package-archives))))
(setq package-review-diff-command
(cons diff-command
'("-u"
"-x" "'*.elc'"
"-x" "'*-autoloads.el'"
"-x" "'*-pkg.el'"
"-x" "'*.info'"
"-x" "'*.texi'"
"-x" "'*.txt'"
"-x" "'*.md'"
"-x" "'*.org'")))
(setq custom-safe-themes t)
@ -178,10 +215,10 @@ effects."
(declare (indent defun))
(let ((eval))
(dolist (element body)
(when-let (((plistp element))
(key (car element))
((eq key :eval))
(val (cadr element)))
(when-let* (((plistp element))
(key (car element))
((eq key :eval))
(val (cadr element)))
(setq eval val
body (delq element body))))
(when eval `(progn ,@body))))
@ -199,6 +236,74 @@ effects."
(indent-region beg end))
(user-error "No active region; will not insert `prot-emacs-comment' here"))))
(defmacro prot-emacs-install (package)
"Install PACKAGE, unless it is installed."
(declare (indent 0))
(unless (symbolp package)
(error "The package `%s' is not a symbol" package))
`(unless (package-installed-p ',package)
(condition-case-unless-debug nil
(progn
(unless package-archive-contents
(package-refresh-contents))
(package-install ',package))
(error (message "Cannot install `%s'; try `M-x package-refresh-contents' first" ',package)))))
(defmacro prot-emacs-install-vc (package &rest vc-args)
"Install PACKAGE with VC-ARGS, unless it is installed."
(declare (indent 0))
(unless (symbolp package)
(error "The package `%s' is not a symbol" package))
`(progn
(unless (package-installed-p ',package)
(condition-case-unless-debug nil
,(cond
((length= vc-args 0)
`(error "VC-ARGS must be 1 or more: `%S'" ',vc-args))
((length> vc-args 1)
`(apply 'package-vc-install ,(append (list package) vc-args)))
(t
`(funcall 'package-vc-install ,(car vc-args))))
(error (message "Cannot install `%s' using VC-ARGS `%S'" ',package ',vc-args))))))
(defmacro prot-emacs-hook (hooks functions &optional remove after)
"For each HOOKS `add-hook' the FUNCTIONS.
With optional REMOVE as non-nil, then `remove-hook' the FUNCTIONS from
HOOKS.
With optional AFTER as the unquoted symbol of a feature, do so after the
given feature is available."
(declare (indent 0))
(cond
((symbolp hooks)
(setq hooks (list hooks)))
((not (proper-list-p hooks))
(error "The hooks are not a list: `%S'" hooks)))
(cond
((symbolp functions)
(setq functions (list functions)))
((not (proper-list-p functions))
(error "The functions are not a list: `%S'" functions)))
(let* ((fn (if remove 'remove-hook 'add-hook))
(body (mapcar
(lambda (h)
(mapcar
(lambda (f) `(,fn ',h #',f))
functions))
hooks))
(hooks nil))
(dolist (element body)
(dolist (hook element)
(push hook hooks)))
(setq hooks (nreverse hooks))
(cond
(after
`(with-eval-after-load ',after ,@hooks))
((length> hooks 1)
`(progn ,@hooks))
(t
(car hooks)))))
(defmacro prot-emacs-keybind (keymap &rest definitions)
"Expand key binding DEFINITIONS for the given KEYMAP.
DEFINITIONS is a sequence of string and command pairs."
@ -208,8 +313,8 @@ DEFINITIONS is a sequence of string and command pairs."
(let ((keys (seq-filter #'stringp definitions))
;; We do accept nil as a definition: it unsets the given key.
(commands (seq-remove #'stringp definitions)))
`(when-let (((keymapp ,keymap))
(map ,keymap))
`(when-let* (((keymapp ,keymap))
(map ,keymap))
,@(mapcar
(lambda (pair)
(let* ((key (car pair))
@ -228,6 +333,19 @@ DEFINITIONS is a sequence of string and command pairs."
;; "C-c b" beframe-prefix-map
;; "C-x k" #'kill-buffer)
(defmacro prot-emacs-autoload (functions file)
"Declare autoloads for FUNCTIONS for FILE."
(declare (indent 0))
(when (symbolp functions)
(setq functions (list functions)))
(unless (listp functions)
(error "The functions must be a list or symbol: %S" functions))
(unless (stringp file)
(error "The file must be a string: %S" file))
(if (length> functions 1)
`(progn ,@(mapcar (lambda (f) `(autoload #',f ,file)) functions))
`(autoload #',(car functions) ,file)))
(defmacro prot-emacs-abbrev (table &rest definitions)
"Expand abbrev DEFINITIONS for the given TABLE.
DEFINITIONS is a sequence of (i) string pairs mapping the
@ -245,24 +363,19 @@ making an abbreviation to a function."
(if (stringp expansion)
`(define-abbrev ,table ,abbrev ,expansion)
`(define-abbrev ,table ,abbrev "" ,expansion))))
(seq-split definitions 2)))
(seq-split definitions 2))
;; Allow abbrevs with a prefix colon, semicolon, or underscore. I demonstrated
;; this here: <https://protesilaos.com/codelog/2024-02-03-emacs-abbrev-mode/>.
(abbrev-table-put ,table :regexp "\\(?:^\\|[\t\s]+\\)\\(?1:[:;_].*\\|.*\\)"))
(error "%s is not an abbrev table" ,table)))
(defvar prot-emacs-package-form-regexp
"^(\\(prot-emacs-keybind\\|prot-emacs-abbrev\\) +'?\\([0-9a-zA-Z-]+\\)"
"Regexp to add packages to `lisp-imenu-generic-expression'.")
(eval-after-load 'lisp-mode
`(add-to-list 'lisp-imenu-generic-expression
(list "Packages" ,prot-emacs-package-form-regexp 2)))
(defconst prot-emacs-font-lock-keywords
'(("(\\(prot-emacs-\\(keybind\\|abbrev\\)\\)\\_>[ \t']*\\(\\(\\sw\\|\\s_\\)+\\)?"
(3 font-lock-variable-name-face nil t))
("(\\(prot-emacs-comment\\)\\_>[ \t']*"
(1 font-lock-preprocessor-face nil t))))
(font-lock-add-keywords 'emacs-lisp-mode prot-emacs-font-lock-keywords)
(defmacro prot-emacs-configure (&rest body)
"Evaluate BODY and catch any errors."
(declare (indent 0))
`(condition-case err
(progn ,@body)
((error user-error quit)
(message "Failed to configure package starting with `%S' because of `%S'" (car ',body) (cdr err)))))
;; For those who use my dotfiles and need an easy way to write their
;; own extras on top of what I already load. The file must exist at
@ -272,6 +385,15 @@ making an abbreviation to a function."
;; preferences BEFORE loading any of the modules.
(load (locate-user-emacs-file "prot-emacs-pre-custom.el") :no-error :no-message)
(defvar prot-display-graphic-p (display-graphic-p)
"When non-nil, the display is graphical.")
(defun prot-emacs-gnome-prefers-dark-p ()
"Return non-nil if GNOME color-scheme is set to dark."
(string-match-p
"dark"
(shell-command-to-string "gsettings get org.gnome.desktop.interface color-scheme")))
(require 'prot-emacs-theme)
(require 'prot-emacs-essentials)
(require 'prot-emacs-modeline)
@ -284,17 +406,13 @@ making an abbreviation to a function."
(require 'prot-emacs-langs)
(require 'prot-emacs-email)
(require 'prot-emacs-web)
(when prot-emacs-load-which-key
(require 'prot-emacs-which-key))
(when prot-emacs-load-icons
(require 'prot-emacs-icons))
(require 'prot-emacs-which-key)
(require 'prot-emacs-icons)
;; For those who use my dotfiles and need an easy way to write their
;; own extras on top of what I already load. The file must exist at
;; ~/.emacs.d/prot-emacs-post-custom.el
;;
;; The purpose of the "post customisations" is to make tweaks to what
;; I already define, such as to change the default theme. See above
;; for the `prot-emacs-pre-custom.el' to make changes BEFORE loading
;; any of my other configurations.
;; The purpose of the "post customisations" is to evaluate arbitrary
;; code AFTER loading all my configurations.
(load (locate-user-emacs-file "prot-emacs-post-custom.el") :no-error :no-message)

View file

@ -1,190 +1,158 @@
;;; General minibuffer settings
(use-package minibuffer
:ensure nil
:config
(prot-emacs-configure
;;;; Completion styles
(setq completion-styles '(basic substring initials flex orderless)) ; also see `completion-category-overrides'
(setq completion-styles '(basic substring initials partial-completion flex)) ; also see `completion-category-overrides'
(setq completion-flex-nospace t)
(setq completion-pcm-leading-wildcard nil) ; Emacs 31
(with-eval-after-load 'orderless
(setq completion-styles (append completion-styles '(orderless)))))
;;;; Completion category overrides
(prot-emacs-configure
;; Reset all the per-category defaults so that (i) we use the
;; standard `completion-styles' and (ii) can specify our own styles
;; in the `completion-category-overrides' without having to
;; explicitly override everything.
(setq completion-category-defaults nil)
;; A non-exhaustve list of known completion categories:
;;
;; - `bookmark'
;; - `buffer'
;; - `charset'
;; - `coding-system'
;; - `color'
;; - `command' (e.g. `M-x')
;; - `customize-group'
;; - `environment-variable'
;; - `expression'
;; - `face'
;; - `file'
;; - `function' (the `describe-function' command bound to `C-h f')
;; - `info-menu'
;; - `imenu'
;; - `input-method'
;; - `kill-ring'
;; - `library'
;; - `minor-mode'
;; - `multi-category'
;; - `package'
;; - `project-file'
;; - `symbol' (the `describe-symbol' command bound to `C-h o')
;; - `theme'
;; - `unicode-name' (the `insert-char' command bound to `C-x 8 RET')
;; - `variable' (the `describe-variable' command bound to `C-h v')
;; - `consult-grep'
;; - `consult-isearch'
;; - `consult-kmacro'
;; - `consult-location'
;; - `embark-keybinding'
;;
(setq completion-category-overrides
;; NOTE 2021-10-25: I am adding `basic' because it works better as a
;; default for some contexts. Read:
;; <https://debbugs.gnu.org/cgi/bugreport.cgi?bug=50387>.
;;
;; `partial-completion' is a killer app for files, because it
;; can expand ~/.l/s/fo to ~/.local/share/fonts.
;;
;; If `basic' cannot match my current input, Emacs tries the
;; next completion style in the given order. In other words,
;; `orderless' kicks in as soon as I input a space or one of its
;; style dispatcher characters.
'((file (styles . (basic partial-completion orderless)))
(bookmark (styles . (basic substring)))
(library (styles . (basic substring)))
(embark-keybinding (styles . (basic substring)))
(imenu (styles . (basic substring orderless)))
(consult-location (styles . (basic substring orderless)))
(kill-ring (styles . (emacs22 orderless)))
(eglot (styles . (emacs22 substring orderless))))))
(prot-minibuffer-missing-categories-mode 1)
;; NOTE 2025-12-02: The `eager-display' and `eager-update' are part of Emacs 31.
(let* ((eager-both '((eager-display . t)
(eager-update . t)))
(eager-both-no-sort (append eager-both (list (cons 'display-sort-function #'identity))))
(eager-update-only '((eager-display . nil)
(eager-update . t)))
(eager-update-only-no-sort (append eager-update-only (list (cons 'display-sort-function #'identity)))))
(setq completion-category-overrides
`((file . (,@eager-update-only
(styles . (partial-completion))
(group-function . ,#'prot-minibuffer-file-group)
(affixation-function . ,#'prot-minibuffer-file-affixate)
(display-sort-function . ,#'prot-minibuffer-file-sort)))
(buffer . (,@eager-update-only
(affixation-function . ,#'prot-minibuffer-buffer-affixate)))
(project-file . (,@eager-update-only
(group-function . ,#'prot-minibuffer-file-group)
(affixation-function . ,#'prot-minibuffer-file-affixate)))
(prot-minibuffer-library . (,@eager-update-only
(annotation-function . ,#'prot-minibuffer-library-annotate)
(display-sort-function . ,#'prot-minibuffer-library-sort)))
(bookmark . (,@eager-update-only
(affixation-function . ,#'prot-minibuffer-bookmark-affixate)))
(command . (,@eager-update-only
(affixation-function . nil) ; so that the `annotation-function' can take effect
(annotation-function . ,#'prot-minibuffer-command-annotate)))
(prot-minibuffer-input-method . (,@eager-update-only
(group-function . ,#'prot-minibuffer-input-method-group)
(display-sort-function . ,#'prot-minibuffer-input-method-sort)))
(symbol-help . ,eager-update-only)
(denote-file . ,eager-update-only)
(cape-abbrev . ,eager-update-only)
(cape-dabbrev . ,eager-update-only)
(cape-dict . ,eager-update-only)
(cape-emoji . ,eager-update-only)
(cape-history . ,eager-update-only)
(cape-keyword . ,eager-update-only)
(cape-super . ,eager-update-only)
(tmr-timer . ,eager-both)
(prot-minibuffer-emoji . ,eager-update-only)
(theme . ,eager-update-only)
(unicode-name . ,eager-update-only)
(prot-minibuffer-pass . ,eager-update-only)
(imenu . ,eager-update-only-no-sort)
(consult-location . ,eager-update-only-no-sort)
(prot-minibuffer-kill-ring . ,eager-update-only-no-sort)))))
;;; Orderless completion style (and prot-orderless.el)
(use-package orderless
:ensure t
:demand t
:after minibuffer
:config
;; Remember to check my `completion-styles' and the
;; `completion-category-overrides'.
(setq orderless-matching-styles '(orderless-prefixes orderless-regexp))
(when prot-emacs-completion-extras
(prot-emacs-configure
(prot-emacs-install orderless)
(require 'orderless)
;; Remember to check my `completion-styles' and the
;; `completion-category-overrides'.
(setq orderless-matching-styles '(orderless-prefixes orderless-regexp))
(setq orderless-smart-case nil)
;; SPC should never complete: use it for `orderless' groups.
;; The `?' is a regexp construct.
:bind ( :map minibuffer-local-completion-map
("SPC" . nil)
("?" . nil)))
(use-package prot-orderless
:ensure nil
:config
(setq orderless-style-dispatchers
'(prot-orderless-literal
prot-orderless-file-ext
prot-orderless-beg-or-end)))
;; SPC should never complete: use it for `orderless' groups.
;; The `?' is a regexp construct.
(prot-emacs-keybind minibuffer-local-completion-map
"SPC" nil
"?" nil)))
(setq completion-ignore-case t)
(setq read-buffer-completion-ignore-case t)
(setq-default case-fold-search t) ; For general regexp
(setq read-file-name-completion-ignore-case t)
(setq minibuffer-history-case-insensitive-variables t)
(use-package mb-depth
:ensure nil
:hook (after-init . minibuffer-depth-indicate-mode)
:config
(setq read-minibuffer-restore-windows nil) ; Emacs 28
(setq enable-recursive-minibuffers t))
(prot-emacs-configure
(setq read-minibuffer-restore-windows nil)
(setq enable-recursive-minibuffers t) ; Emacs 28
(minibuffer-depth-indicate-mode 1))
(use-package minibuf-eldef
:ensure nil
:hook (after-init . minibuffer-electric-default-mode)
:config
(setq minibuffer-default-prompt-format " [%s]")) ; Emacs 29
(use-package rfn-eshadow
:ensure nil
:hook (minibuffer-setup . cursor-intangible-mode)
:config
;; Not everything here comes from rfn-eshadow.el, but this is fine.
(prot-emacs-configure
(setq minibuffer-default-prompt-format " [%s]") ; Emacs 29
(minibuffer-electric-default-mode 1))
(prot-emacs-configure
(setq resize-mini-windows t)
(setq read-answer-short t) ; also check `use-short-answers' for Emacs28
(setq echo-keystrokes 0.25)
(setq kill-ring-max 60) ; Keep it small
(add-hook 'minibuffer-setup-hook #'cursor-intangible-mode)
;; Do not allow the cursor to move inside the minibuffer prompt. I
;; got this from the documentation of Daniel Mendler's Vertico
;; package: <https://github.com/minad/vertico>.
(setq minibuffer-prompt-properties
'(read-only t cursor-intangible t face minibuffer-prompt))
;; MCT has a variant of this built-in.
(unless (eq prot-emacs-completion-ui 'mct)
;; Add prompt indicator to `completing-read-multiple'. We display
;; [`completing-read-multiple': <separator>], e.g.,
;; [`completing-read-multiple': ,] if the separator is a comma. This
;; is adapted from the README of the `vertico' package by Daniel
;; Mendler. I made some small tweaks to propertize the segments of
;; the prompt.
(defun crm-indicator (args)
(cons (format "[`completing-read-multiple': %s] %s"
(propertize
(replace-regexp-in-string
"\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" ""
crm-separator)
'face 'error)
(car args))
(cdr args)))
(advice-add #'completing-read-multiple :filter-args #'crm-indicator))
(setq crm-prompt (format "%s %%p" (propertize "[%d]" 'face 'shadow))) ; Emacs 31
(file-name-shadow-mode 1))
(use-package minibuffer
:ensure nil
:demand t
:config
(setq completions-format 'one-column)
(setq completion-show-help nil)
(setq completion-auto-help 'always)
(setq completion-auto-select nil)
(setq completions-detailed t)
(setq completion-show-inline-help nil)
(setq completions-max-height 6)
(setq completions-header-format (propertize "%s candidates:\n" 'face 'bold-italic))
(setq completions-highlight-face 'completions-highlight)
(setq minibuffer-completion-auto-choose t)
(setq minibuffer-visible-completions t) ; Emacs 30
(setq completions-sort 'historical)
(prot-emacs-configure
(prot-emacs-hook
(completion-list-mode-hook minibuffer-setup-hook)
prot-common-truncate-lines-silently)
(setq completions-group-format
(concat
(propertize (make-string 20 ? ) 'face 'completions-group-separator)
(propertize " %s " 'face 'completions-group-title)
(propertize " " 'face 'completions-group-separator 'display '(space :align-to right))))
(unless prot-emacs-completion-ui
(prot-emacs-keybind minibuffer-local-completion-map
"<up>" #'minibuffer-previous-line-completion
"<down>" #'minibuffer-next-line-completion)
(prot-minibuffer-completions-mode 1)
(add-hook 'completion-list-mode-hook #'prot-common-truncate-lines-silently)))
(prot-emacs-keybind completion-in-region-mode-map
"M-n" #'minibuffer-next-completion
"M-p" #'minibuffer-previous-completion)
(prot-emacs-keybind minibuffer-local-completion-map
"<down>" #'minibuffer-next-line-completion
"<up>" #'minibuffer-previous-line-completion
"C-h C-h" #'prot-minibuffer-completions-describe-at-point ; overrides `help-for-help'
"C-<tab>" #'prot-minibuffer-choose-completion-no-exit
"RET" #'prot-minibuffer-choose-completion-exit)
(prot-emacs-keybind completion-list-mode-map
"C-h C-h" #'prot-minibuffer-completions-describe-at-point ; overrides `help-for-help'
"C-<tab>" #'prot-minibuffer-choose-completion-no-exit
"RET" #'prot-minibuffer-choose-completion-dwim)))
;;;; `savehist' (minibuffer and related histories)
(use-package savehist
:ensure nil
:hook (after-init . savehist-mode)
:config
(prot-emacs-configure
(setq savehist-file (locate-user-emacs-file "savehist"))
(setq history-length 100)
(setq history-delete-duplicates t)
(setq savehist-save-minibuffer-history t)
(add-to-list 'savehist-additional-variables 'kill-ring))
(with-eval-after-load 'savehist
(add-to-list 'savehist-additional-variables 'kill-ring))
(savehist-mode 1))
(use-package dabbrev
:ensure nil
:commands (dabbrev-expand dabbrev-completion)
:config
(prot-emacs-configure
;;;; `dabbrev' (dynamic word completion (dynamic abbreviations))
(setq dabbrev-abbrev-char-regexp "\\sw\\|\\s_")
(setq dabbrev-abbrev-skip-leading-regexp "[$*/=~']")
@ -195,31 +163,41 @@
(setq dabbrev-check-other-buffers t)
(setq dabbrev-eliminate-newlines t)
(setq dabbrev-upcase-means-case-search t)
(setq dabbrev-ignored-buffer-modes
'(archive-mode image-mode docview-mode pdf-view-mode)))
(setq dabbrev-ignored-buffer-modes '(archive-mode image-mode docview-mode pdf-view-mode)))
;;;; `abbrev' (Abbreviations, else Abbrevs)
(use-package abbrev
:ensure nil
;; message-mode derives from text-mode, so we don't need a separate
;; hook for it.
:hook ((text-mode prog-mode git-commit-mode) . abbrev-mode)
:config
(prot-emacs-configure
(require 'prot-abbrev)
(setq only-global-abbrevs nil)
(define-key global-map (kbd "C-x a u") #'unexpand-abbrev)
;; Because the *scratch* buffer is produced before we load this, we
;; have to explicitly activate the mode there.
(when-let* ((scratch (get-buffer "*scratch*")))
(with-current-buffer scratch
(abbrev-mode 1)))
;; By default, abbrev asks for confirmation on whether to use
;; `abbrev-file-name' to save abbrevations. I do not need that, nor
;; do I want it.
(remove-hook 'save-some-buffers-functions #'abbrev--possibly-save)
(prot-emacs-hook
( text-mode-hook prog-mode-hook git-commit-mode-hook
vc-git-log-edit-mode-hook message-mode-hook)
abbrev-mode)
(prot-emacs-abbrev global-abbrev-table
"meweb" "https://protesilaos.com"
"megit" "https://github.com/protesilaos"
"mehub" "https://github.com/protesilaos"
"meclone" "git@github.com/protesilaos/"
"melab" "https://gitlab.com/protesilaos"
"medrive" "hyper://5cr7mxac8o8aymun698736tayrh1h4kbqf359cfk57swjke716gy/"
";web" "https://protesilaos.com"
";git" "https://github.com/protesilaos"
";hub" "https://github.com/protesilaos"
";clone" "git@github.com/protesilaos/"
";lab" "https://gitlab.com/protesilaos"
";drive" "hyper://5cr7mxac8o8aymun698736tayrh1h4kbqf359cfk57swjke716gy/")
";time" #'prot-abbrev-current-time
";date" #'prot-abbrev-current-date
";jitsi" #'prot-abbrev-jitsi-link)
(prot-emacs-abbrev text-mode-abbrev-table
"asciidoc" "AsciiDoc"
@ -239,24 +217,20 @@
"texmacs" "TeXmacs"
"typescript" "TypeScript"
"visavis" "vis-à-vis"
"deja" "déjà"
"voila" "voilà"
"youtube" "YouTube"
";up" "🙃"
";uni" "🦄"
";laugh" "🤣"
";smile" "😀"
";sun" "☀️")
;; Allow abbrevs with a prefix colon, semicolon, or underscore. I demonstrated
;; this here: <https://protesilaos.com/codelog/2024-02-03-emacs-abbrev-mode/>.
(abbrev-table-put global-abbrev-table :regexp "\\(?:^\\|[\t\s]+\\)\\(?1:[:;_].*\\|.*\\)")
(with-eval-after-load 'text-mode
(abbrev-table-put text-mode-abbrev-table :regexp "\\(?:^\\|[\t\s]+\\)\\(?1:[:;_].*\\|.*\\)"))
";update" #'prot-abbrev-update-html)
(with-eval-after-load 'org
(prot-emacs-abbrev org-mode-abbrev-table
";dev" "{{{development-version}}}")
(abbrev-table-put org-mode-abbrev-table :regexp "\\(?:^\\|[\t\s]+\\)\\(?1:[:;_].*\\|.*\\)"))
";dev" "{{{development-version}}}"
";key" #'prot-abbrev-org-macro-key
";cmd" #'prot-abbrev-org-macro-key-command))
(with-eval-after-load 'message
(prot-emacs-abbrev message-mode-abbrev-table
@ -265,79 +239,184 @@
"niceday" "Have a nice day,\nProtesilaos (or simply \"Prot\")"
"abest" "All the best,\nProt"
"bregards" "Best regards,\nProt"
"nday" "Have a nice day,\nProt"
"nosrht" "P.S. I am phasing out SourceHut: <https://protesilaos.com/codelog/2024-01-27-sourcehut-no-more/>.
Development continues on GitHub with GitLab as a mirror."))
"nday" "Have a nice day,\nProt"))
;; The `prot-emacs-abbrev' macro, which simplifies how we use
;; `define-abbrev', does not only expand a static text. It can take
;; a pair of string and function to trigger the latter when the
;; former is inserted. Think of it like the basis of a simplistic
;; templating system.
(require 'prot-abbrev)
(prot-emacs-abbrev global-abbrev-table
"metime" #'prot-abbrev-current-time
"medate" #'prot-abbrev-current-date
"mejitsi" #'prot-abbrev-jitsi-link
";time" #'prot-abbrev-current-time
";date" #'prot-abbrev-current-date
";jitsi" #'prot-abbrev-jitsi-link)
(with-eval-after-load 'markdown-mode
(prot-emacs-abbrev markdown-mode-abbrev-table
";vlog" "---
title: \"#\"
excerpt: \"#\"
layout: vlog
mediaid: \"#\"
---
"
";post" "---
title: \"#\"
excerpt: \"#\"
---
"
";poem" "---
title: \"#\"
excerpt: \"Just read the poem. No further comment.\"
---
"))
(prot-emacs-abbrev text-mode-abbrev-table
";update" #'prot-abbrev-update-html)
;; Because the *scratch* buffer is produced before we load this, we
;; have to explicitly activate the mode there.
(when-let ((scratch (get-buffer "*scratch*")))
(with-current-buffer scratch
(abbrev-mode 1)))
;; By default, abbrev asks for confirmation on whether to use
;; `abbrev-file-name' to save abbrevations. I do not need that, nor
;; do I want it.
(remove-hook 'save-some-buffers-functions #'abbrev--possibly-save))
(with-eval-after-load 'vc-git
(prot-emacs-abbrev vc-git-log-edit-mode-abbrev-table
";update" #'prot-abbrev-update-html)))
;;; Corfu (in-buffer completion popup)
(use-package corfu
:ensure t
:hook (after-init . global-corfu-mode)
;; I also have (setq tab-always-indent 'complete) for TAB to complete
;; when it does not need to perform an indentation change.
:bind (:map corfu-map ("<tab>" . corfu-complete))
:config
(setq corfu-preview-current nil)
(setq corfu-min-width 20)
(when (or (eq prot-emacs-completion-in-buffer 'corfu)
(eq prot-emacs-completion-in-buffer 'corfu-completion-preview))
(prot-emacs-configure
(prot-emacs-install corfu)
(setq corfu-popupinfo-delay '(1.25 . 0.5))
(corfu-popupinfo-mode 1) ; shows documentation after `corfu-popupinfo-delay'
(setq corfu-preview-current nil)
(setq corfu-min-width 20)
;; Sort by input history (no need to modify `corfu-sort-function').
(with-eval-after-load 'savehist
(corfu-history-mode 1)
(add-to-list 'savehist-additional-variables 'corfu-history)))
(setq corfu-popupinfo-delay '(1.25 . 0.5))
(corfu-popupinfo-mode 1) ; shows documentation after `corfu-popupinfo-delay'
(global-corfu-mode 1)
;; I also have (setq tab-always-indent 'complete) for TAB to complete
;; when it does not need to perform an indentation change.
(define-key corfu-map (kbd "<tab>") #'corfu-complete)
;; Sort by input history (no need to modify `corfu-sort-function').
(with-eval-after-load 'savehist
(corfu-history-mode 1)
(add-to-list 'savehist-additional-variables 'corfu-history))))
(unless (eq prot-emacs-completion-in-buffer 'company)
(prot-emacs-configure
(prot-emacs-install cape)
(defun prot/cape-super-set-local (capfs &optional individual-capfs)
"Set `completion-at-point-functions' to current value plus CAPFS.
Treat CAPFS and the default value as a super CAPF. Then append the
INDIVIDUAL-CAPFS to the list."
(let* ((all-for-super (append completion-at-point-functions capfs))
(all-minus-global (delq t all-for-super))
(cape-super (apply #'cape-capf-super all-minus-global)))
(setq-local completion-at-point-functions (append (list cape-super) individual-capfs (list t)))))
(defun prot/cape-prog-setup ()
"Set up Cape for programming."
(prot/cape-super-set-local '(cape-dabbrev cape-abbrev) '(cape-file)))
(add-hook 'prog-mode-hook #'prot/cape-prog-setup)
(defun prot/cape-text-setup ()
"Set up Cape for prose."
(prot/cape-super-set-local '(cape-dict cape-dabbrev cape-abbrev cape-emoji) '(cape-file)))
(setq completion-at-point-functions '(cape-dabbrev cape-file))
(add-hook 'text-mode-hook #'prot/cape-text-setup)))
(when (eq prot-emacs-completion-in-buffer 'company)
(prot-emacs-configure
(prot-emacs-install company)
(prot-emacs-install company-statistics)
;; NOTE 2026-07-10: with the latest version of Company, (setq
;; tab-always-indent 'complete) is supported out-of-the-box, which
;; means that I can finally disable the automatic popup feature.
(setq company-idle-delay nil)
(setq company-tooltip-limit 6)
(setq company-tooltip-minimum-width 25)
(setq company-tooltip-align-annotations t)
(setq company-dabbrev-code-completion-styles t) ; use the `completion-styles'
(setq company-transformers '(company-sort-by-occurrence
company-sort-by-backend-importance
company-sort-prefer-same-case-prefix))
(setq company-frontends '(company-childframe-unless-just-one-frontend
company-preview-if-just-one-frontend
company-echo-metadata-frontend))
(setq company-backends '((company-capf company-dabbrev) company-files))
(define-advice company-show-doc-buffer (:around (&rest args) prot)
(let ((help-window-select nil)
(display-buffer-alist nil)
(display-buffer-overriding-action
'((display-buffer-reuse-mode-window display-buffer-below-selected)
(window-height . 0.3)
(post-command-select-window . nil))))
(apply args)))
(defun prot/company-prog-backends ()
"Set `company-backends' locally to my preferred value for `prog-mode' and derivatives."
(setq-local company-backends '((company-capf company-dabbrev company-abbrev) company-files)))
(defun prot/company-text-backends ()
"Set `company-backends' locally to my preferred value for `text-mode' and derivatives."
(setq-local company-backends '((company-dabbrev company-abbrev company-ispell company-capf) company-files)))
(prot-emacs-hook (prog-mode-hook comint-mode-hook) prot/company-prog-backends)
(prot-emacs-hook (prog-mode-hook text-mode-hook comint-mode-hook) company-mode)
(add-hook 'text-mode-hook #'prot/company-text-backends)
(with-eval-after-load 'company
(prot-emacs-keybind company-active-map
"C-s" #'company-filter-candidates
"<escape>" #'company-abort
"<tab>" #'company-complete-selection
"<up>" #'company-select-previous-or-abort
"<down>" #'company-select-next-or-abort)
(company-statistics-mode 1))))
(when (or (eq prot-emacs-completion-in-buffer 'completion-preview)
(eq prot-emacs-completion-in-buffer 'corfu-completion-preview))
(prot-emacs-configure
(setq completion-preview-exact-match-only nil)
(setq completion-preview-minimum-symbol-length 2)
(setq completion-preview-idle-delay nil)
(setq completion-preview-ignore-case t)
(prot-emacs-hook
(prog-mode-hook log-edit-mode-hook git-commit-mode-hook comint-mode-hook)
completion-preview-mode)
(with-eval-after-load 'completion-preview
(setq completion-preview-commands
(seq-remove
(lambda (command)
(memq command '(delete-backward-char backward-delete-char-untabify)))
completion-preview-commands))
(prot-emacs-keybind completion-preview-active-mode-map
"M-i" #'completion-preview-insert-word
"M-n" #'completion-preview-next-candidate
"M-p" #'completion-preview-prev-candidate
"M-<return>" #'completion-preview-insert
;; With TAB we effectively defer to another frontend to show
;; more completion candidates at once.
"<tab>" #'completion-preview-complete))
(with-eval-after-load 'org
(add-to-list 'completion-preview-commands #'org-self-insert-command))))
;;; Enhanced minibuffer commands (consult.el)
(when prot-emacs-completion-extras
(use-package consult
:ensure t
:hook (completion-list-mode . consult-preview-at-point-mode)
:bind
( :map global-map
("M-g M-g" . consult-goto-line)
("M-K" . consult-keep-lines) ; M-S-k is similar to M-S-5 (M-%)
("M-F" . consult-focus-lines) ; same principle
("M-s M-b" . consult-buffer)
("M-s M-f" . consult-find)
("M-s M-g" . consult-grep)
("M-s M-h" . consult-history)
("M-s M-i" . consult-imenu)
("M-s M-l" . consult-line)
("M-s M-m" . consult-mark)
("M-s M-y" . consult-yank-pop)
("M-s M-s" . consult-outline)
:map consult-narrow-map
("?" . consult-narrow-help))
:config
(prot-emacs-configure
(prot-emacs-install consult)
(prot-emacs-keybind global-map
"M-g M-g" #'consult-goto-line
"M-s M-b" #'consult-buffer
"M-s M-f" #'consult-find
"M-s M-g" #'consult-grep
"M-s M-h" #'consult-history
"M-s M-i" #'consult-imenu
"M-s M-l" #'consult-line
"M-s M-m" #'consult-mark
"M-s M-y" #'consult-yank-pop
"M-s M-s" #'consult-outline)
(setq consult-line-numbers-widen t)
;; (setq completion-in-region-function #'consult-completion-in-region)
(setq consult-async-min-input 3)
@ -349,123 +428,39 @@ Development continues on GitHub with GitLab as a mirror."))
"-path */.git* -prune "
"-or -path */.cache* -prune )"))
(setq consult-preview-key 'any)
(setq consult-project-function nil) ; always work from the current directory (use `cd' to switch directory)
(add-to-list 'consult-mode-histories '(vc-git-log-edit-mode . log-edit-comment-ring))
;; see my `pulsar' package: <https://protesilaos.com/emacs/pulsar>
(setq consult-after-jump-hook nil) ; reset it to avoid conflicts with my function
(prot-emacs-hook
consult-after-jump-hook
(pulsar-recenter-top pulsar-reveal-entry)
nil
pulsar)))
(require 'consult-imenu) ; the `imenu' extension is in its own file
(with-eval-after-load 'pulsar
;; see my `pulsar' package: <https://protesilaos.com/emacs/pulsar>
(setq consult-after-jump-hook nil) ; reset it to avoid conflicts with my function
(dolist (fn '(pulsar-recenter-top pulsar-reveal-entry))
(add-hook 'consult-after-jump-hook fn)))))
;;; Extended minibuffer actions and more (embark.el and prot-embark.el)
;;; Extended minibuffer actions and more (embark.el)
(when prot-emacs-completion-extras
(use-package embark
:ensure t
:defer 1
:config
(setq embark-confirm-act-all nil)
;; The prot-embark.el has an advice to further simplify the
;; minimal indicator. It shows cycling, which I never want to see
;; or do.
(setq embark-mixed-indicator-both nil)
(setq embark-mixed-indicator-delay 1.0)
(setq embark-indicators '(embark-mixed-indicator embark-highlight-indicator))
(setq embark-verbose-indicator-nested nil) ; I think I don't have them, but I do not want them either
(setq embark-verbose-indicator-buffer-sections '(bindings))
(setq embark-verbose-indicator-excluded-actions
'(embark-cycle embark-act-all embark-collect embark-export embark-insert))
(prot-emacs-configure
(prot-emacs-install embark)
;; I never cycle and want to disable the damn thing. Normally, a
;; nil value disables a key binding but here that value is
;; interpreted as the binding for `embark-act'. So I just add
;; some obscure key that I do not have. I absolutely do not want
;; to cycle!
(setq embark-cycle-key "<XF86Travel>")
(add-hook 'embark-collect-mode-hook #'prot-common-truncate-lines-silently)
;; I do not want `embark-org' and am not sure what is loading it.
;; So I just unsert all the keymaps... This is the nuclear option
;; but here we are.
(with-eval-after-load 'embark-org
(defvar prot/embark-org-keymaps
'(embark-org-table-cell-map
embark-org-table-map
embark-org-link-copy-map
embark-org-link-map
embark-org-src-block-map
embark-org-item-map
embark-org-plain-list-map
embark-org-export-in-place-map)
"List of Embark keymaps for Org.")
(prot-emacs-keybind minibuffer-local-map
"C-c C-c" #'embark-collect
"C-c C-e" #'embark-export)
;; Reset `prot/embark-org-keymaps'.
(seq-do
(lambda (keymap)
(set keymap (make-sparse-keymap)))
prot/embark-org-keymaps)))
;; Needed for correct exporting while using Embark with Consult commands.
(prot-emacs-install embark-consult)
;; I define my own keymaps because I only use a few functions in a
;; limited number of contexts.
(use-package prot-embark
:ensure nil
:after embark
:bind
( :map global-map
("C-," . prot-embark-act-no-quit)
("C-." . prot-embark-act-quit)
:map embark-collect-mode-map
("C-," . prot-embark-act-no-quit)
("C-." . prot-embark-act-quit)
:map minibuffer-local-filename-completion-map
("C-," . prot-embark-act-no-quit)
("C-." . prot-embark-act-quit))
:config
(setq embark-keymap-alist
'((buffer prot-embark-buffer-map)
(command prot-embark-command-map)
(expression prot-embark-expression-map)
(file prot-embark-file-map)
(function prot-embark-function-map)
(identifier prot-embark-identifier-map)
(package prot-embark-package-map)
(region prot-embark-region-map)
(symbol prot-embark-symbol-map)
(url prot-embark-url-map)
(variable prot-embark-variable-map)
(t embark-general-map))))
;; Needed for correct exporting while using Embark with Consult
;; commands.
(use-package embark-consult
:ensure t
:after (embark consult)))
(with-eval-after-load 'consult
(require 'embark-consult))))
;;; Detailed completion annotations (marginalia.el)
(use-package marginalia
:ensure t
:defer 1
:config
(setq marginalia-max-relative-age 0) ; absolute time
(marginalia-mode 1))
;;;; Custom completion annotations
(use-package prot-marginalia
:ensure nil
:after marginalia
:config
(setq marginalia-annotator-registry
'((bookmark prot-marginalia-bookmark)
(buffer prot-marginalia-buffer)
(command marginalia-annotate-command)
(function prot-marginalia-symbol)
(symbol prot-marginalia-symbol)
(variable prot-marginalia-symbol)
(face marginalia-annotate-face)
(imenu marginalia-annotate-imenu)
(package prot-marginalia-package)
(unicode-name marginalia-annotate-char))))
(when prot-emacs-completion-extras
(prot-emacs-configure
(prot-emacs-install marginalia)
(setq marginalia-max-relative-age 0) ; absolute time
(marginalia-mode 1)))
;;; The minibuffer user interface (mct, vertico, or none)
(when prot-emacs-completion-ui

View file

@ -1,147 +1,131 @@
;;; Dired file manager and prot-dired.el extras
(use-package dired
:ensure nil
:commands (dired)
:config
(prot-emacs-configure
(setq dired-recursive-copies 'always)
(setq dired-recursive-deletes 'always)
(setq delete-by-moving-to-trash t))
(use-package dired
:ensure nil
:commands (dired)
:config
(setq dired-listing-switches
"-AGFhlv --group-directories-first --time-style=long-iso"))
(setq dired-listing-switches "-AGFhlv --group-directories-first --time-style=long-iso")
(use-package dired
:ensure nil
:commands (dired)
:config
(setq dired-dwim-target t))
(setq dired-dwim-target t)
(use-package dired
:ensure nil
:commands (dired)
:config
(setq dired-guess-shell-alist-user ; those are the suggestions for ! and & in Dired
'(("\\.\\(png\\|jpe?g\\|tiff\\)" "feh" "xdg-open")
("\\.\\(mp[34]\\|m4a\\|ogg\\|flac\\|webm\\|mkv\\)" "mpv" "xdg-open")
(".*" "xdg-open"))))
(setq dired-guess-shell-alist-user ; those are the suggestions for ! and & in Dired
'(("\\.\\(png\\|jpe?g\\|tiff\\)" "feh *" "feh" "xdg-open")
("\\.\\(mp[34]\\|m4a\\|ogg\\|flac\\|webm\\|mkv\\)" "mpv *" "mpv" "xdg-open")
(".*" "xdg-open")))
(use-package dired
:ensure nil
:commands (dired)
:config
(setq dired-auto-revert-buffer #'dired-directory-changed-p) ; also see `dired-do-revert-buffer'
(setq dired-make-directory-clickable t) ; Emacs 29.1
(setq dired-free-space nil) ; Emacs 29.1
(setq dired-mouse-drag-files t) ; Emacs 29.1
(prot-emacs-configure
(with-eval-after-load 'dired
(setq dired-auto-revert-buffer #'dired-directory-changed-p) ; also see `dired-do-revert-buffer'
(setq dired-make-directory-clickable t) ; Emacs 29.1
(setq dired-free-space nil) ; Emacs 29.1
(setq dired-mouse-drag-files t) ; Emacs 29.1
(add-hook 'dired-mode-hook #'dired-hide-details-mode)
(add-hook 'dired-mode-hook #'hl-line-mode)
(prot-emacs-hook dired-mode-hook (dired-hide-details-mode hl-line-mode))
;; In Emacs 29 there is a binding for `repeat-mode' which lets you
;; repeat C-x C-j just by following it up with j. For me, this is a
;; problem as j calls `dired-goto-file', which I often use.
(define-key dired-jump-map (kbd "j") nil))
;; In Emacs 29 there is a binding for `repeat-mode' which lets you
;; repeat C-x C-j just by following it up with j. For me, this is a
;; problem as j calls `dired-goto-file', which I often use.
(define-key dired-jump-map (kbd "j") nil)))
(use-package dired-aux
:ensure nil
:after dired
:bind
( :map dired-mode-map
("C-+" . dired-create-empty-file)
("M-s f" . nil)
("C-<return>" . dired-do-open) ; Emacs 30
("C-x v v" . dired-vc-next-action)) ; Emacs 28
:config
(setq dired-isearch-filenames 'dwim)
(setq dired-create-destination-dirs 'ask) ; Emacs 27
(setq dired-vc-rename-file t) ; Emacs 27
(setq dired-do-revert-buffer (lambda (dir) (not (file-remote-p dir)))) ; Emacs 28
(setq dired-create-destination-dirs-on-trailing-dirsep t)) ; Emacs 29
(prot-emacs-configure
(with-eval-after-load 'dired
(require 'dired-aux)
(use-package dired-x
:ensure nil
:after dired
:bind
( :map dired-mode-map
("I" . dired-info))
:config
(setq dired-clean-up-buffers-too t)
(setq dired-clean-confirm-killing-deleted-buffers t)
(setq dired-x-hands-off-my-keys t) ; easier to show the keys I use
(setq dired-bind-man nil)
(setq dired-bind-info nil))
(prot-emacs-keybind dired-mode-map
"C-+" #'dired-create-empty-file
"M-s f" #'nil
"C-<return>" #'dired-do-open ; Emacs 30
"C-x v v" #'dired-vc-next-action) ; Emacs 28
(use-package prot-dired
:ensure nil
:hook (dired-mode . prot-dired-setup-imenu)
:bind
( :map dired-mode-map
("i" . prot-dired-insert-subdir) ; override `dired-maybe-insert-subdir'
("/" . prot-dired-limit-regexp)
("C-c C-l" . prot-dired-limit-regexp)
("M-n" . prot-dired-subdirectory-next)
("C-c C-n" . prot-dired-subdirectory-next)
("C-c C-p" . prot-dired-subdirectory-previous)
("M-s G" . prot-dired-grep-marked-files) ; M-s g is `prot-search-grep'
("M-p" . prot-dired-subdirectory-previous)))
(setq dired-isearch-filenames 'dwim)
(setq dired-create-destination-dirs 'ask) ; Emacs 27
(setq dired-vc-rename-file t) ; Emacs 27
(setq dired-do-revert-buffer (lambda (dir) (not (file-remote-p dir)))) ; Emacs 28
(setq dired-create-destination-dirs-on-trailing-dirsep t) ; Emacs 29
(use-package dired-subtree
:ensure t
:bind
( :map dired-mode-map
("<tab>" . dired-subtree-toggle)
("<backtab>" . dired-subtree-remove)) ; S-TAB
:config
(setq dired-subtree-use-backgrounds nil))
(require 'dired-x)
(use-package wdired
:ensure nil
:commands (wdired-change-to-wdired-mode)
:config
(define-key dired-mode-map (kbd "I") #'dired-info)
(setq dired-clean-up-buffers-too t)
(setq dired-clean-confirm-killing-deleted-buffers t)
(setq dired-x-hands-off-my-keys t) ; easier to show the keys I use
(setq dired-bind-man nil)
(setq dired-bind-info nil)))
(prot-emacs-configure
(with-eval-after-load 'dired
(require 'prot-dired)
(add-hook 'dired-mode #'prot-dired-setup-imenu)
(prot-emacs-keybind dired-mode-map
"f" nil ; disable `dired-find-file' as it is on RET already
"i" #'prot-dired-insert-subdir ; override `dired-maybe-insert-subdir'
"/" #'prot-dired-limit-regexp
"C-M-<return>" #'prot-dired-open-directory-externally ; C-<return> is `dired-do-open'
"C-c C-l" #'prot-dired-limit-regexp
"M-n" #'prot-dired-subdirectory-next
"C-c C-s" #'prot-dired-search-flat-list
"C-c C-r" #'prot-dired-search-flat-list-since-days
"C-c C-n" #'prot-dired-subdirectory-next
"C-c C-p" #'prot-dired-subdirectory-previous
"M-s G" #'prot-dired-grep-marked-files ; M-s g is `prot-search-grep'
"M-p" #'prot-dired-subdirectory-previous)))
(prot-emacs-configure
(prot-emacs-install dired-subtree)
(with-eval-after-load 'dired
(prot-emacs-keybind dired-mode-map
"<tab>" #'dired-subtree-toggle
"TAB" #'dired-subtree-toggle
"<backtab>" #'dired-subtree-remove
"S-TAB" #'dired-subtree-remove)
(setq dired-subtree-use-backgrounds nil)))
(prot-emacs-configure
(setq wdired-allow-to-change-permissions t)
(setq wdired-create-parent-directories t))
(use-package image-dired
:ensure nil
:commands (image-dired)
:bind
( :map image-dired-thumbnail-mode-map
("<return>" . image-dired-thumbnail-display-external))
:config
(setq image-dired-thumbnail-storage 'standard)
(setq image-dired-external-viewer "xdg-open")
(setq image-dired-thumb-size 80)
(setq image-dired-thumb-margin 2)
(setq image-dired-thumb-relief 0)
(setq image-dired-thumbs-per-row 4))
(prot-emacs-configure
(with-eval-after-load 'image-dired
(define-key image-dired-thumbnail-mode-map (kbd "<return>") #'image-dired-thumbnail-display-external)
(setq image-dired-thumbnail-storage 'standard)
(setq image-dired-external-viewer "xdg-open")
(setq image-dired-thumb-size 80)
(setq image-dired-thumb-margin 2)
(setq image-dired-thumb-relief 0)
(setq image-dired-thumbs-per-row 4)))
;;; Automatically preview Dired file at point (dired-preview.el)
;; One of my packages: <https://protesilaos.com/emacs>
(use-package dired-preview
:ensure t
:hook (dired-mode . (lambda ()
(when (string-match-p "Pictures" default-directory)
(dired-preview-mode 1))))
:config
;; These are all set to their default values. I keep them here for
;; reference.
(setq dired-preview-max-size (* (expt 2 20) 6))
(setq dired-preview-delay 0.3)
(setq dired-preview-ignored-extensions-regexp
(concat "\\."
"\\(mkv\\|" "webm\\|" "mp4\\|" "mp3\\|" "ogg\\|" "m4a\\|"
"gz\\|" "zst\\|" "tar\\|" "xz\\|" "rar\\|" "zip\\|"
"iso\\|" "epub\\|" "\\)")))
(prot-emacs-configure
(prot-emacs-install dired-preview)
(with-eval-after-load 'dired
(require 'dired-preview)
(add-hook 'dired-mode-hook (lambda ()
(when (string-match-p "Pictures" default-directory)
(dired-preview-mode 1))))
(define-key dired-mode-map (kbd "V") #'dired-preview-mode)
(setq dired-preview-trigger-on-start nil)
(setq dired-preview-max-size (* (expt 2 20) 10))
(setq dired-preview-delay 0.5)
(setq dired-preview-ignored-extensions-regexp
(concat "\\."
"\\(gz\\|"
"zst\\|"
"tar\\|"
"xz\\|"
"rar\\|"
"zip\\|"
"iso\\|"
"epub"
"\\)"))
(setq dired-preview-display-action-alist #'dired-preview-display-action-alist-below)))
;;; dired-like mode for the trash (trashed.el)
(use-package trashed
:ensure t
:commands (trashed)
:config
(prot-emacs-configure
(prot-emacs-install trashed)
(setq trashed-action-confirmer 'y-or-n-p)
(setq trashed-use-header-line t)
(setq trashed-sort-key '("Date deleted" . t))
@ -149,17 +133,18 @@
;;; Play back media with Dired (mandoura.el)
;; This is yet another package of mine: <https://protesilaos.com/emacs>
(use-package mandoura
;; The :vc keyword is part of Emacs 30. Read the manual for what keywords it reads: (info "(emacs) Fetching Package Sources")
:vc ( :url "https://github.com/protesilaos/mandoura")
:bind
( :map global-map
("M-<AudioPlay>" . mandoura-return-track-title-and-time)
("M-<XF86AudioPlay>" . mandoura-return-track-title-and-time)
:map dired-mode-map
("M-<return>" . mandoura-play-files)
("M-RET" . mandoura-play-files))
:config
(setq mandoura-saved-playlist-directory "~/Music/playlists/"))
(prot-emacs-configure
(prot-emacs-install-vc
mandoura
"https://github.com/protesilaos/mandoura.git")
(with-eval-after-load 'dired
(require 'mandoura)
(prot-emacs-keybind global-map
"M-<AudioPlay>" #'mandoura-return-track-title-and-time
"M-<XF86AudioPlay>" #'mandoura-return-track-title-and-time)
(prot-emacs-keybind dired-mode-map
"M-<return>" #'mandoura-play-playlist
"M-RET" #'mandoura-play-playlist)
(setq mandoura-saved-playlist-directory "~/Music/playlists/")))
(provide 'prot-emacs-dired)

View file

@ -0,0 +1,32 @@
;;; The Doric themes
(prot-emacs-configure
(prot-emacs-install doric-themes)
(prot-emacs-keybind global-map
"<f5>" #'doric-themes-rotate
"C-<f5>" #'doric-themes-select)
(doric-themes-load-random
(if (prot-emacs-gnome-prefers-dark-p)
'dark
'light)))
;; For testing purposes
(prot-emacs-comment
(:eval nil)
(add-to-list 'load-path "~/Git/Projects/doric-themes/")
(require 'doric-themes)
(prot-emacs-keybind global-map
"<f5>" #'doric-themes-rotate
"C-<f5>" #'doric-themes-select)
(doric-themes-load-random
(if (prot-emacs-gnome-prefers-dark-p)
'dark
'light)))
(provide 'prot-emacs-doric-themes)

View file

@ -1,15 +1,21 @@
;;; The Ef (εὖ) themes
;; The themes are customisable. Read the manual:
;; <https://protesilaos.com/emacs/ef-themes>.
(use-package ef-themes
:ensure t
:demand t
:bind ("<f5>" . ef-themes-select)
:config
(setq ef-themes-variable-pitch-ui t
ef-themes-mixed-fonts t
ef-themes-headings ; read the manual's entry of the doc string
(prot-emacs-configure
(prot-emacs-install ef-themes)
(ef-themes-take-over-modus-themes-mode 1)
(prot-emacs-keybind global-map
"<f5>" #'modus-themes-rotate
"C-<f5>" #'modus-themes-select
"M-<f5>" #'modus-themes-load-random)
(setq modus-themes-variable-pitch-ui t
modus-themes-mixed-fonts t
modus-themes-bold-constructs t
modus-themes-italic-constructs t
modus-themes-to-rotate nil ; defaults to the return value of `modus-themes-get-themes'
modus-themes-headings ; read the manual's entry of the doc string
'((0 . (variable-pitch light 1.9))
(1 . (variable-pitch light 1.8))
(2 . (variable-pitch regular 1.7))
@ -22,78 +28,28 @@
(agenda-structure . (variable-pitch light 1.9))
(t . (variable-pitch 1.1))))
;; The `ef-themes' provide lots of themes. I want to pick one at
;; random when I start Emacs: the `ef-themes-load-random' does just
;; that (it can be called interactively as well). I just check with
;; my desktop environment to determine if the choice should be about
;; a light or a dark theme. Those functions are in my init.el.
(if (prot-emacs-theme-environment-dark-p)
(ef-themes-load-random 'dark)
(ef-themes-load-random 'light)))
(if (prot-emacs-gnome-prefers-dark-p)
(modus-themes-load-random 'dark)
(modus-themes-load-random 'light)))
;; NOTE: For testing purposes
(prot-emacs-comment
(:eval nil)
(progn
(mapc #'disable-theme custom-enabled-themes)
(add-to-list 'load-path "/home/prot/Git/Projects/ef-themes/")
(add-to-list 'load-path "~/Git/Projects/modus-themes/")
(add-to-list 'load-path "~/Git/Projects/ef-themes/")
(require 'ef-themes)
(load-theme 'ef-arbutus t t)
(load-theme 'ef-autumn t t)
(load-theme 'ef-bio t t)
(load-theme 'ef-cherie t t)
(load-theme 'ef-cyprus t t)
(load-theme 'ef-dark t t)
(load-theme 'ef-day t t)
(load-theme 'ef-deuteranopia-dark t t)
(load-theme 'ef-deuteranopia-light t t)
(load-theme 'ef-duo-dark t t)
(load-theme 'ef-duo-light t t)
(load-theme 'ef-frost t t)
(load-theme 'ef-kassio t t)
(load-theme 'ef-light t t)
(load-theme 'ef-melissa-dark t t)
(load-theme 'ef-melissa-light t t)
(load-theme 'ef-night t t)
(load-theme 'ef-rosa t t)
(load-theme 'ef-spring t t)
(load-theme 'ef-summer t t)
(load-theme 'ef-symbiosis t t)
(load-theme 'ef-trio-dark t t)
(load-theme 'ef-trio-light t t)
(load-theme 'ef-tritanopia-dark t t)
(load-theme 'ef-tritanopia-light t t)
(load-theme 'ef-winter t t)
(require 'modus-themes)
(require 'ef-themes)
(setq ef-themes-headings ; read the manual's entry or the doc string
'((0 . (variable-pitch light 1.9))
(1 . (variable-pitch light 1.8))
(2 . (variable-pitch light 1.7))
(3 . (variable-pitch semilight 1.6))
(4 . (variable-pitch semilight 1.5))
(5 . (variable-pitch regular 1.4))
(6 . (variable-pitch regular 1.3))
(7 . (variable-pitch 1.2)) ; absence of weight means `bold'
(agenda-date . (semilight 1.5))
(agenda-structure . (variable-pitch light 1.9))
(t . (variable-pitch 1.1))))
(ef-themes-take-over-modus-themes-mode 1)
;; They are nil by default...
(setq ef-themes-mixed-fonts t
ef-themes-variable-pitch-ui nil)
(prot-emacs-keybind global-map
"<f5>" #'modus-themes-rotate
"C-<f5>" #'modus-themes-select
"M-<f5>" #'modus-themes-load-random)
(mapcar (lambda (theme)
(add-to-list
'custom-theme-load-path
(concat "/home/prot/Git/Projects/ef-themes/" (symbol-name theme) "-theme.el")))
(ef-themes--list-enabled-themes))
(if (prot-emacs-gnome-prefers-dark-p)
(modus-themes-load-random 'dark)
(modus-themes-load-random 'light)))
(if (prot-emacs-theme-environment-dark-p)
(ef-themes-load-random 'dark)
(ef-themes-load-random 'light))
(define-key global-map (kbd "<f5>") #'ef-themes-select)))
(provide 'prot-emacs-ef-themes)
(provide 'prot-emacs-ef-themes)

View file

@ -1,44 +1,29 @@
;;;; File with authentication credentials (`auth-source')
(use-package auth-source
:ensure nil
:defer t
:config
(prot-emacs-configure
(setq auth-sources '("~/.authinfo.gpg")
user-full-name "Protesilaos Stavrou"
user-mail-address "public@protesilaos.com"))
user-full-name "Protesilaos"
user-mail-address "prot@protesilaos.com"))
;;;; Encryption settings (`mm-encode' and `mml-sec')
(use-package mm-encode
:ensure nil
:defer t
:config
(prot-emacs-configure
(setq mm-encrypt-option nil ; use 'guided for both if you need more control
mm-sign-option nil))
mm-sign-option nil)
(use-package mml-sec
:ensure nil
:defer t
:config
(setq mml-secure-openpgp-encrypt-to-self t
mml-secure-openpgp-sign-with-sender t
mml-secure-smime-encrypt-to-self t
mml-secure-smime-sign-with-sender t))
mml-secure-smime-sign-with-sender t)
;;;; Message composition (`message')
(use-package message
:ensure nil
:defer t
:hook
(message-setup . message-sort-headers)
:config
(add-hook 'message-setup-hook #'message-sort-headers)
(setq mail-user-agent 'message-user-agent
message-mail-user-agent t) ; use `mail-user-agent'
(setq mail-header-separator "--text follows this line--")
(setq message-elide-ellipsis "\n> [... %l lines elided]\n")
(setq compose-mail-user-agent-warnings nil)
(setq message-signature "Protesilaos Stavrou\nhttps://protesilaos.com\n"
(setq message-signature "Protesilaos\nhttps://protesilaos.com\n"
mail-signature message-signature)
(setq message-citation-line-function #'message-insert-formatted-citation-line)
(setq message-citation-line-format (concat "> From: %f\n"
@ -48,27 +33,37 @@
(setq message-confirm-send nil)
(setq message-kill-buffer-on-exit t)
;; (add-to-list 'mm-body-charset-encoding-alist '(utf-8 . base64))
(setq message-wide-reply-confirm-recipients nil))
(setq message-forward-as-mime t)
(setq message-wide-reply-confirm-recipients nil)
(defun prot/message-copy-body ()
"Copy the body of the current message buffer."
(interactive)
(save-excursion
(let ((beginning (message-goto-body))
(end (progn
(message-goto-signature)
(forward-line -2)
(point))))
(kill-new (buffer-substring-no-properties beginning end))
(message "Copied the body of the current message buffer")
(setq this-command 'kill-ring-save))))
(with-eval-after-load 'message
;; Override `message-insert-signature'.
(define-key message-mode-map (kbd "C-c C-w") #'prot/message-copy-body)))
;;;; Add attachments from Dired (`gnus-dired' does not require `gnus')
(use-package gnus-dired
:ensure nil
:after message
:hook
(dired-mode . turn-on-gnus-dired-mode))
(prot-emacs-configure
(add-hook 'dired-mode-hook #'turn-on-gnus-dired-mode))
;;;; `sendmail' (mail transfer agent)
(use-package sendmail
:ensure nil
:after message
:config
(setq send-mail-function 'sendmail-send-it
;; ;; NOTE 2023-08-08: We do not need this if we have the Arch
;; ;; Linux `msmtp-mta' package installed: it replaces the
;; ;; generic sendmail executable with msmtp.
;;
;; sendmail-program (executable-find "msmtp")
message-sendmail-envelope-from 'header))
(prot-emacs-configure
(with-eval-after-load 'message
(setq send-mail-function #'smtpmail-send-it)
(setq smtpmail-smtp-server "mail.gandi.net")
(setq smtpmail-smtp-service 587)
(setq smtpmail-stream-type 'starttls)))
(when (executable-find "notmuch")
(require 'prot-emacs-notmuch))

View file

@ -1,179 +1,239 @@
;;; Essential configurations
(use-package emacs
:ensure nil
:demand t
:config
(prot-emacs-configure
;;;; General settings and common custom functions (prot-simple.el)
(setq backward-delete-char-untabify-method nil)
(setq blink-matching-paren nil)
(setq custom-unlispify-tag-names nil)
(setq delete-pair-blink-delay 0.1) ; Emacs28 -- see `prot-simple-delete-pair-dwim'
(setq help-window-select t)
(setq next-error-recenter '(4)) ; center of the window
(setq find-library-include-other-files nil) ; Emacs 29
(setq remote-file-name-inhibit-delete-by-moving-to-trash t) ; Emacs 30
(setq remote-file-name-inhibit-auto-save t) ; Emacs 30
(setq tramp-connection-timeout (* 60 10)) ; seconds
(setq save-interprogram-paste-before-kill t)
(setq mode-require-final-newline 'visit-save)
(setq-default truncate-partial-width-windows nil)
(setq eval-expression-print-length nil)
(setq kill-do-not-save-duplicates t)
(setq duplicate-line-final-position -1 ; both are Emacs 29
duplicate-region-final-position -1)
(setq scroll-error-top-bottom t)
(setq delete-pair-push-mark t) ; Emacs 31
(setq echo-keystrokes-help nil) ; Emacs 30
(setq epa-keys-select-method 'minibuffer) ; Emacs 30
(setq eval-expression-print-length nil)
(setq find-library-include-other-files nil) ; Emacs 29
(setq help-window-select t)
(setq help-window-keep-selected t) ; Emacs 29
(setq kill-do-not-save-duplicates t)
(setq mode-require-final-newline 'visit-save)
(setq next-error-recenter '(4)) ; center of the window
(setq remote-file-name-inhibit-auto-save t) ; Emacs 30
(setq remote-file-name-inhibit-delete-by-moving-to-trash t) ; Emacs 30
(setq save-interprogram-paste-before-kill t)
(setq tramp-connection-timeout (* 60 10)) ; seconds
(setq trusted-content '("~/Git/Projects/" "~/Coach/")) ; Emacs 30
(setq truncate-partial-width-windows nil)
;; Keys I unbind here are either to avoid accidents or to bind them
;; elsewhere later in the configuration.
:bind
( :map global-map
("<insert>" . nil)
("<menu>" . nil)
("C-z" . nil) ; I have a window manager, thanks!
("C-x C-z" . nil) ; same idea as above
("C-x C-c" . nil) ; avoid accidentally exiting Emacs
("C-x C-c C-c" . save-buffers-kill-emacs) ; more cumbersome, less error-prone
("C-h h" . nil) ; Never show that "hello" file
("M-`" . nil)
("M-o" . delete-blank-lines) ; alias for C-x C-o
("M-SPC" . cycle-spacing)
("M-z" . zap-up-to-char) ; NOT `zap-to-char'
("M-c" . capitalize-dwim)
("M-l" . downcase-dwim) ; "lower" case
("M-u" . upcase-dwim)
("M-=" . count-words)
("C-x O" . next-multiframe-window)
("C-h K" . describe-keymap) ; overrides `Info-goto-emacs-key-command-node'
("C-h u" . apropos-user-option)
("C-h F" . apropos-function) ; lower case is `describe-function'
("C-h V" . apropos-variable) ; lower case is `describe-variable'
("C-h L" . apropos-library) ; lower case is `view-lossage'
("C-h c" . describe-char) ; overrides `describe-key-briefly'
:map prog-mode-map
("C-M-d" . up-list) ; confusing name for what looks like "down" to me
("<C-M-backspace>" . backward-kill-sexp)
(prot-emacs-keybind global-map
"<f2>" #'toggle-input-method ; F2 overrides that two-column gimmick. Sorry, but no!
"<insert>" nil
"<menu>" nil
"C-M-w" nil ; never use it
"C-x C-d" nil ; never use it
"C-x C-v" nil ; never use it
"C-z" nil ; I have a window manager, thanks!
"C-x C-z" nil ; same idea as above
"C-x C-c" nil ; avoid accidentally exiting Emacs
"C-x C-c C-c" #'save-buffers-kill-emacs ; more cumbersome, less error-prone
"C-h h" nil ; Never show that "hello" file
"M-`" nil
"M-o" #'delete-blank-lines ; alias for C-x C-o
"M-SPC" #'cycle-spacing
"M-z" #'zap-up-to-char ; NOT `zap-to-char'
"M-c" #'capitalize-dwim
"M-l" #'downcase-dwim ; "lower" case
"M-u" #'upcase-dwim
"M-=" #'count-words
"C-x O" #'next-multiframe-window
"C-h K" #'describe-keymap ; overrides `Info-goto-emacs-key-command-node'
"C-h u" #'apropos-user-option
"C-h F" #'apropos-function ; lower case is `describe-function'
"C-h V" #'apropos-variable ; lower case is `describe-variable'
"C-h L" #'apropos-library ; lower case is `view-lossage'
"C-h c" #'describe-char) ; overrides `describe-key-briefly'
;; Keymap for buffers (Emacs28)
:map ctl-x-x-map
("f" . follow-mode) ; override `font-lock-update'
("r" . rename-uniquely)
("l" . visual-line-mode)))
(prot-emacs-keybind prog-mode-map
"C-M-d" #'up-list ; confusing name for what looks like "down" to me
"<C-M-backspace>" #'backward-kill-sexp)
;; Keymap for buffers (Emacs28)
(prot-emacs-keybind ctl-x-x-map
"f" #'follow-mode ; override `font-lock-update'
"r" #'rename-uniquely
"l" #'visual-line-mode))
(prot-emacs-configure
(require 'prot-common)
(defvar prot/fundamental-mode-hook nil
"Normal hook for `fundamental-mode' (which is missing by default).")
(defun prot/fundamental-mode-run-hook (&rest args)
"Apply ARGS and then run `prot/fundamental-mode-hook'."
(apply args)
(run-hooks 'prot/fundamental-mode-hook))
(advice-add #'fundamental-mode :around #'prot/fundamental-mode-run-hook)
(prot-emacs-hook
(text-mode-hook prog-mode-hook dired-mode-hook prot/fundamental-mode-hook hexl-mode-hook comint-mode-hook)
prot-common-truncate-lines-silently)
(use-package prot-common
:ensure nil
:functions (prot-common-truncate-lines-silently)
:hook ((fundamental-mode text-mode prog-mode) . prot-common-truncate-lines-silently)
:config
;; NEVER tell me which key can call a command that I specifically
;; invoked with M-x: I have a good reason to use it that way.
(advice-add #'execute-extended-command--describe-binding-msg :override #'prot-common-ignore))
(use-package prot-simple
:ensure nil
:demand t
:config
(prot-emacs-configure
(require 'prot-simple)
(setq prot-simple-date-specifier "%F")
(setq prot-simple-time-specifier "%R %z")
(advice-add #'save-buffers-kill-emacs :before #'prot-simple-display-unsaved-buffers-on-exit)
(with-eval-after-load 'pulsar
(add-hook 'prot-simple-file-to-register-jump-hook #'pulsar-recenter-center)
(add-hook 'prot-simple-file-to-register-jump-hook #'pulsar-reveal-entry))
:bind
( ("ESC ESC" . prot-simple-keyboard-quit-dwim)
("C-g" . prot-simple-keyboard-quit-dwim)
("C-M-SPC" . prot-simple-mark-sexp) ; will be overriden by `expreg' if tree-sitter is available
;; All `prot-simple-override-mode' does is activate a keymap.
;; Below I add keys to that map. Because the mode is enabled
;; globally, those keys take precedence over the ones specified by
;; any given major mode. In principle, this means that my keys will
;; always work (though technically they can be overriden by another
;; minor mode, depending on which one is evaluated last).
(prot-simple-override-mode 1)
(defun prot/simple-hex-highlight ()
(when-let* ((file buffer-file-name)
((derived-mode-p 'emacs-lisp-mode))
((string-match-p "-theme" file)))
(prot-simple-hex-color-mode 1)))
(add-hook 'emacs-lisp-mode-hook #'prot/simple-hex-highlight)
(define-key ctl-x-x-map (kbd "c") #'prot-simple-hex-color-mode) ; C-x x c
(prot-emacs-keybind prot-simple-override-mode-map
"C-a" #'prot-simple-duplicate-line-or-region ; "again" mnemonic, overrides `move-beginning-of-line'
"C-d" #'prot-simple-delete-line ; overrides `delete-char'
"C-v" #'prot-simple-multi-line-below ; overrides `scroll-up-command'
"<next>" #'prot-simple-multi-line-below ; overrides `scroll-up-command'
"M-v" #'prot-simple-multi-line-above ; overrides `scroll-down-command'
"<prior>" #'prot-simple-multi-line-above ; overrides `scroll-down-command'
"C-M-i" #'prot-simple-indent-dwim ; overrides `completion-at-point'
"C-M-\\" #'prot-simple-indent-dwim ; overrides `indent-region'
"C-M-c" #'completion-at-point) ; overrides `exit-recursive-edit'
(prot-emacs-keybind global-map
"C-h h" #'prot-simple-describe-at-point
"<escape>" #'prot-simple-keyboard-quit-dwim
"C-g" #'prot-simple-keyboard-quit-dwim
"C-M-SPC" #'prot-simple-mark-sexp
"C-x 0" #'prot-simple-delete-window-dwim ; overrides `delete-window'
;; Commands for lines
("M-k" . prot-simple-kill-line-backward)
("C-S-d" . prot-simple-duplicate-line-or-region)
("C-S-w" . prot-simple-copy-line)
("C-S-y" . prot-simple-yank-replace-line-or-region)
("C-v" . prot-simple-multi-line-below) ; overrides `scroll-up-command'
("<next>" . prot-simple-multi-line-below) ; overrides `scroll-up-command'
("M-v" . prot-simple-multi-line-above) ; overrides `scroll-down-command'
("<prior>" . prot-simple-multi-line-above) ; overrides `scroll-down-command'
("<C-return>" . prot-simple-new-line-below)
("<C-S-return>" . prot-simple-new-line-above)
"C-S-d" #'prot-simple-delete-line-backward
"C-S-k" #'prot-simple-kill-line-backward
"M-k" #'prot-simple-copy-line-forward
"M-K" #'prot-simple-copy-line-backward
"M-j" #'delete-indentation
"C-w" #'prot-simple-kill-region
"M-w" #'prot-simple-kill-ring-save
"C-S-w" #'prot-simple-copy-line
"C-S-y" #'prot-simple-yank-replace-line-or-region
"<C-return>" #'prot-simple-new-line-below
"<C-S-return>" #'prot-simple-new-line-above
"C-x x a" #'prot-simple-auto-fill-visual-line-mode ; auto-fill/visual-line toggle
;; Commands for text insertion or manipulation
("C-=" . prot-simple-insert-date)
("C-<" . prot-simple-escape-url-dwim)
;; "C->" prot-simple-insert-line-prefix-dwim
("M-Z" . prot-simple-zap-to-char-backward)
"C-=" #'prot-simple-insert-date
"C-<" #'prot-simple-escape-url-dwim
"C->" #'prot-simple-escape-url-dwim
"M-Z" #'prot-simple-zap-to-char-backward
;; Commands for object transposition
("C-S-p" . prot-simple-move-above-dwim)
("C-S-n" . prot-simple-move-below-dwim)
("C-t" . prot-simple-transpose-chars)
("C-x C-t" . prot-simple-transpose-lines)
("C-S-t" . prot-simple-transpose-paragraphs)
("C-x M-t" . prot-simple-transpose-sentences)
("C-M-t" . prot-simple-transpose-sexps)
("M-t" . prot-simple-transpose-words)
"C-S-p" #'prot-simple-move-above-dwim
"C-S-n" #'prot-simple-move-below-dwim
"C-t" #'prot-simple-transpose-chars
"C-x C-t" #'prot-simple-transpose-lines
"C-S-t" #'prot-simple-transpose-paragraphs
"C-x M-t" #'prot-simple-transpose-sentences
"C-M-t" #'prot-simple-transpose-sexps
"M-t" #'prot-simple-transpose-words
;; Commands for paragraphs
("M-Q" . prot-simple-unfill-region-or-paragraph)
"M-Q" #'prot-simple-unfill-region-or-paragraph
;; Commands for windows and pages
("C-x o" . prot-simple-other-window)
("C-x n k" . prot-simple-delete-page-delimiters)
("C-x M-r" . prot-simple-swap-window-buffers)
"C-x o" #'prot-simple-other-window
"C-x n k" #'prot-simple-delete-page-delimiters
"M-r" #'window-layout-transpose ; Emacs 31 override `move-to-window-line-top-bottom'
"M-S-r" #'rotate-windows-back ; Emacs 31
;; Commands for buffers
("<C-f2>" . prot-simple-rename-file-and-buffer)
("C-x k" . prot-simple-kill-buffer-current)
("C-x K" . kill-buffer) ; leaving this here to contrast with the above
("M-s b" . prot-simple-buffers-major-mode)
("M-s v" . prot-simple-buffers-vc-root)
;; Commands for files
("C-x r ." . prot-simple-file-to-register)))
"C-<f2>" #'prot-simple-rename-file-and-buffer
"M-<f2>" #'prot-simple-copy-current-path
"C-x k" #'prot-simple-kill-buffer-dwim
"C-x K" #'kill-buffer ; leaving this here to contrast with the above
"M-s b" #'prot-simple-buffers-major-mode
"M-s v" #'prot-simple-buffers-vc-root))
;;;; Scratch buffers per major mode (prot-scratch.el)
(use-package prot-scratch
:ensure nil
:bind ("C-c s" . prot-scratch-buffer)
:config
(setq prot-scratch-default-mode 'text-mode))
(prot-emacs-configure
(setq prot-scratch-default-mode 'text-mode)
(autoload #'prot-scratch-buffer "prot-scratch")
(define-key global-map (kbd "C-c s") #'prot-scratch-buffer))
;;;; Insert character pairs (prot-pair.el)
(use-package prot-pair
:ensure nil
:bind
(("C-'" . prot-pair-insert)
("M-'" . prot-pair-insert)
("M-\\" . prot-pair-delete)))
(prot-emacs-configure
(prot-emacs-autoload
(prot-pair-insert prot-pair-insert-directly prot-pair-delete)
"prot-pair")
(prot-emacs-keybind global-map
"C-'" #'prot-pair-insert
"M-'" #'prot-pair-insert-directly
"M-\\" #'prot-pair-delete))
;;;; Comments (prot-comment.el)
(use-package prot-comment
:ensure nil
:init
(prot-emacs-configure
(setq comment-empty-lines t)
(setq comment-fill-column nil)
(setq comment-multi-line t)
(setq comment-style 'multi-line)
(setq-default comment-column 0)
(setq prot-comment-comment-keywords '("TODO" "NOTE" "XXX" "REVIEW" "FIXME"))
(setq prot-comment-comment-keywords '("TODO" "NOTE" "FIXME"))
(setq prot-comment-timestamp-format-concise "%F")
(setq prot-comment-timestamp-format-verbose "%F %T %z")
:bind
(("C-;" . prot-comment)
("C-x C-;" . prot-comment-timestamp-keyword)))
(prot-emacs-autoload
(prot-comment prot-comment-timestamp-keyword)
"prot-comment")
(prot-emacs-keybind global-map
"C-;" #'prot-comment
"M-;" #'prot-comment ; overrides `comment-dwim'
"C-x C-;" #'prot-comment-timestamp-keyword))
;;;; Prefix keymap (prot-prefix.el)
(use-package prot-prefix
:ensure nil
:bind-keymap
;; F2 overrides that two-column gimmick. Sorry, but no.
(("<insert>" . prot-prefix)
("<f2>" . prot-prefix)
("C-z" . prot-prefix)))
(prot-emacs-configure
(require 'prot-prefix)
(prot-emacs-keybind global-map
"<insert>" #'prot-prefix
"C-z" #'prot-prefix))
(prot-emacs-configure
(define-key global-map (kbd "C-x C-r") #'recentf-open) ; override `find-file-read-only'
(with-eval-after-load 'recentf
(setq recentf-max-saved-items 100)
(setq recentf-max-menu-items 25) ; I don't use the `menu-bar-mode', but this is good to know
(setq recentf-save-file-modes nil)
(setq recentf-keep nil)
(setq recentf-auto-cleanup nil)
(setq recentf-initialize-file-name-history nil)
(setq recentf-filename-handlers nil)
(setq recentf-show-file-shortcuts-flag nil)
(recentf-mode 1)))
;;;; Mouse and mouse wheel behaviour
(use-package mouse
:ensure nil
:hook (after-init . mouse-wheel-mode)
:config
(prot-emacs-configure
(mouse-wheel-mode 1)
;; Some of these variables are defined in places other than
;; mouse.el, but this is fine.
(setq mouse-autoselect-window t) ; complements the auto-selection of my tiling window manager
(setq focus-follows-mouse t)
;; In Emacs 27+, use Control + mouse wheel to scale text.
(setq mouse-wheel-scroll-amount
@ -187,16 +247,16 @@
mouse-wheel-follow-mouse t)
;; Scrolling behaviour
(setq-default scroll-preserve-screen-position t
scroll-conservatively 1 ; affects `scroll-step'
scroll-margin 0
next-screen-context-lines 0))
(setq scroll-error-top-bottom t
scroll-preserve-screen-position t
scroll-conservatively 1 ; affects `scroll-step'
scroll-margin 0
next-screen-context-lines 0))
;;;; Repeatable key chords (repeat-mode)
(use-package repeat
:ensure nil
:hook (after-init . repeat-mode)
:config
(prot-emacs-configure
(repeat-mode 1)
(setq repeat-on-final-keystroke t
repeat-exit-timeout 5
repeat-exit-key "<escape>"
@ -208,11 +268,8 @@
set-mark-command-repeat-pop t))
;;;; Built-in bookmarking framework (bookmark.el)
(use-package bookmark
:ensure nil
:commands (bookmark-set bookmark-jump bookmark-bmenu-list)
:hook (bookmark-bmenu-mode . hl-line-mode)
:config
(prot-emacs-configure
(add-hook 'bookmark-bmenu-mode-hook #'hl-line-mode)
(setq bookmark-use-annotations nil)
(setq bookmark-automatically-show-annotations nil)
(setq bookmark-fringe-mark nil) ; Emacs 29 to hide bookmark fringe icon
@ -223,33 +280,21 @@
(setq bookmark-save-flag 1))
;;;; Registers (register.el)
(use-package register
:ensure nil
:defer t ; its commands are autoloaded, so this will be loaded then
:config
(setq register-preview-delay 0.8
register-preview-function #'register-preview-default)
(with-eval-after-load 'savehist
(add-to-list 'savehist-additional-variables 'register-alist)))
(setq register-preview-delay 0.5
register-preview-function #'register-preview-default)
;;;; Auto revert mode
(use-package autorevert
:ensure nil
:hook (after-init . global-auto-revert-mode)
:config
(prot-emacs-configure
(global-auto-revert-mode 1)
(setq auto-revert-verbose t))
;;;; Delete selection
(use-package delsel
:ensure nil
:hook (after-init . delete-selection-mode))
(prot-emacs-configure
(delete-selection-mode 1))
;;;; Tooltips (tooltip-mode)
(use-package tooltip
:ensure nil
:hook (after-init . tooltip-mode)
:config
(prot-emacs-configure
(tooltip-mode 1)
(setq tooltip-delay 0.5
tooltip-short-delay 0.5
x-gtk-use-system-tooltips t
@ -260,10 +305,7 @@
(no-special-glyphs . t))))
;;;; Display current time
(use-package time
:ensure nil
:hook (after-init . display-time-mode)
:config
(prot-emacs-configure
(setq display-time-format " %a %e %b, %H:%M ")
;;;; Covered by `display-time-format'
;; (setq display-time-24hr-format t)
@ -286,60 +328,62 @@
(format-time-string display-time-format now)
'face 'display-time-date-and-time
'help-echo (format-time-string "%a %b %e, %Y" now))
" ")))
" "))
(display-time-mode 1))
;;;; World clock (M-x world-clock)
(use-package time
:ensure nil
:commands (world-clock)
:config
(setq display-time-world-list t)
(prot-emacs-configure
(setq zoneinfo-style-world-list ; M-x shell RET timedatectl list-timezones
'(("America/Los_Angeles" "Los Angeles")
("America/Vancouver" "Vancouver")
("Canada/Pacific" "Canada/Pacific")
("America/Chicago" "Chicago")
("Brazil/Acre" "Rio Branco")
("America/New_York" "New York")
("Canada/Atlantic" "Canada/Atlantic")
("Brazil/East" "Brasília")
("UTC" "UTC")
("Europe/Lisbon" "Lisbon")
("Europe/Brussels" "Brussels")
("Europe/Athens" "Athens")
("Asia/Riyadh" "Riyadh")
("Asia/Tehran" "Tehran")
("Asia/Tbilisi" "Tbilisi")
("Asia/Yekaterinburg" "Yekaterinburg")
("Asia/Kolkata" "Kolkata")
("Asia/Singapore" "Singapore")
("Asia/Shanghai" "Shanghai")
("Asia/Seoul" "Seoul")
("Asia/Tokyo" "Tokyo")
("Asia/Vladivostok" "Vladivostok")
("Australia/Brisbane" "Brisbane")
("Australia/Sydney" "Sydney")
("Pacific/Auckland" "Auckland")))
'(("America/Los_Angeles" "🇺🇸 Los Angeles")
("America/Vancouver" "🇨🇦 Vancouver")
("America/Mexico_City" "🇲🇽 Mexico City")
("America/Denver" "🇺🇸 Denver")
("America/Chicago" "🇺🇸 Chicago")
("America/New_York" "🇺🇸 New York")
("America/Toronto" "🇨🇦 Toronto")
("America/Montreal" "🇨🇦 Montreal")
("UTC" "🌐 UTC")
("Europe/Lisbon" "🇵🇹 Lisbon")
("Europe/London" "🇬🇧 London")
("Europe/Paris" "🇫🇷 Paris")
("Europe/Brussels" "🇧🇪 Brussels")
("Europe/Berlin" "🇩🇪 Berlin")
("Europe/Athens" "🇬🇷 Athens")
("Asia/Nicosia" "🇨🇾 Lefkosia")
("Asia/Istanbul" "🇹🇷 Istanbul")
("Asia/Baghdad" "🇸🇾 Baghdad")
("Asia/Riyadh" "🇸🇦 Riyadh")
("Asia/Tehran" "🇮🇷 Tehran")
("Asia/Tbilisi" "🇬🇪 Tbilisi")
("Asia/Tashkent" "🇺🇿 Tashkent")
("Asia/Karachi" "🇵🇰 Karachi")
("Asia/Kolkata" "🇮🇳 Kolkata")
("Asia/Dhaka" "🇧🇩 Dhaka")
("Asia/Phnom_Penh" "🇰🇭 Phnom Penh")
("Asia/Ho_Chi_Minh" "🇻🇳 Ho Chi Minh")
("Asia/Singapore" "🇸🇬 Singapore")
("Asia/Shanghai" "🇨🇳 Shanghai")
("Asia/Seoul" "🇰🇷 Seoul")
("Asia/Tokyo" "🇯🇵 Tokyo")
("Australia/Brisbane" "🇦🇺 Brisbane")
("Australia/Sydney" "🇦🇺 Sydney")
("Pacific/Auckland" "🇳🇿 Auckland")))
;; All of the following variables are for Emacs 28
(setq world-clock-sort-order nil) ; Emacs 31
(setq world-clock-list t)
(setq world-clock-time-format "%R %z (%Z) %A %d %B")
(setq world-clock-time-format "%z %R %a %d %b (%Z)")
(setq world-clock-buffer-name "*world-clock*") ; Placement handled by `display-buffer-alist'
(setq world-clock-timer-enable t)
(setq world-clock-timer-second 60))
;;;; `man' (manpages)
(use-package man
:ensure nil
:commands (man)
:config
(prot-emacs-configure
(setq Man-notify-method 'pushy)) ; does not obey `display-buffer-alist'
;;;; `proced' (process monitor, similar to `top')
(use-package proced
:ensure nil
:commands (proced)
:config
(prot-emacs-configure
(setq proced-auto-update-flag 'visible) ; Emacs 30 supports more the `visible' value
(setq proced-enable-color-flag t) ; Emacs 29
(setq proced-auto-update-interval 5)
@ -347,31 +391,24 @@
(setq proced-filter 'user))
;;;; Emacs server (allow emacsclient to connect to running session)
(use-package server
:ensure nil
:defer 1
:config
(prot-emacs-configure
(setq server-client-instructions nil)
(unless (server-running-p)
(require 'server)
(unless (or (server-running-p) (daemonp))
(server-start)))
;;; Substitute
;; Another package of mine... Video demo:
;; <https://protesilaos.com/codelog/2023-01-16-emacs-substitute-package-demo/>.
(use-package substitute
:ensure t
(prot-emacs-configure
(prot-emacs-install substitute)
(require 'substitute)
;; Produce a message after the substitution that reports on what
;; happened. It is a single line, like "Substituted `TARGET' with
;; `SUBSTITUTE' N times across the buffer.
:hook (substitute-post-replace . substitute-report-operation)
:bind
;; The mnemonic for the prefix is that M-# (or M-S-3) is close to
;; M-% (or M-S-5).
(("M-# s" . substitute-target-below-point) ; Forward motion like isearch (C-s)
("M-# r" . substitute-target-above-point) ; Backward motion like isearch (C-r)
("M-# d" . substitute-target-in-defun) ; "defun" mnemonic
("M-# b" . substitute-target-in-buffer)) ; "buffer" mnemonic
:config
(add-hook 'substitute-post-replace-hook #'substitute-report-operation)
;; Set this to non-nil to highlight all occurrences of the current
;; target.
(setopt substitute-highlight t)
@ -379,190 +416,114 @@
;; Set this to t if you want to always treat the letter casing
;; literally. Otherwise each command accepts a `C-u' prefix
;; argument to do this on-demand.
(setq substitute-fixed-letter-case nil))
(setq substitute-fixed-letter-case nil)
(use-package goto-chg
:ensure t
:bind
(("C-(" . goto-last-change)
("C-)" . goto-last-change-reverse)))
;; C-c s is occupied by `prot-scratch-buffer'.
(define-key global-map (kbd "C-c r") #'substitute-prefix-map))
;;; Mark syntactic constructs efficiently if tree-sitter is available (expreg)
(when (and (treesit-available-p) prot-emacs-treesitter-extras)
(use-package expreg
:ensure t
:functions (prot/expreg-expand prot/expreg-expand-dwim)
;; There is also an `expreg-contract' command, though I have no use for it.
:bind ("C-M-SPC" . prot/expreg-expand-dwim) ; overrides `mark-sexp'
:config
(defun prot/expreg-expand (n)
"Expand to N syntactic units, defaulting to 1 if none is provided interactively."
(interactive "p")
(dotimes (_ n)
(expreg-expand)))
(defun prot/expreg-expand-dwim ()
"Do-What-I-Mean `expreg-expand' to start with symbol or word.
If over a real symbol, mark that directly, else start with a
word. Fall back to regular `expreg-expand'."
(interactive)
(let ((symbol (bounds-of-thing-at-point 'symbol)))
(cond
((equal (bounds-of-thing-at-point 'word) symbol)
(prot/expreg-expand 1))
(symbol (prot/expreg-expand 2))
(t (expreg-expand)))))))
;;; Visualise undo ring (`vundo')
(use-package vundo
:ensure t
:defer 1
:bind
( :map vundo-mode-map
("C-/" . vundo-backward)
("C-?" . vundo-forward)
("u" . vundo-backward)
("U" . vundo-forward)
("g" . vundo-goto-last-saved)
("." . vundo-goto-last-saved)
("h" . vundo-backward)
("j" . vundo-next)
("k" . vundo-previous)
("l" . vundo-forward))
:config
(setq vundo-glyph-alist vundo-unicode-symbols)
(defvar prot/vundo-undo-functions '(undo undo-only undo-redo)
"List of undo functions to check if we need to visualise the undo ring.")
(defvar prot/vundo-undo-command #'undo
"Command to call if we are not going to visualise the undo ring.")
(defun prot/vundo-if-repeat-undo (&rest args)
"Use `vundo' if the last command is among `prot/vundo-undo-functions'.
In other words, start visualising the undo ring if we are going
to be cycling through the edits."
(interactive)
(if (and (member last-command prot/vundo-undo-functions)
(not undo-in-region))
(call-interactively 'vundo)
(apply args)))
(mapc
(lambda (fn)
(advice-add fn :around #'prot/vundo-if-repeat-undo))
prot/vundo-undo-functions)
(with-eval-after-load 'pulsar
(add-hook 'vundo-post-exit-hook #'pulsar-pulse-line-green)))
(prot-emacs-configure
(prot-emacs-install goto-chg)
(prot-emacs-keybind global-map
"C-(" #'goto-last-change
"C-)" #'goto-last-change-reverse))
;;; TMR May Ring (tmr is used to set timers)
;; Read the manual: <https://protesilaos.com/emacs/tmr>.
(use-package tmr
:ensure t
:bind
(("C-c t t" . tmr)
("C-c t T" . tmr-with-description)
("C-c t l" . tmr-tabulated-view) ; "list timers" mnemonic
("C-c t c" . tmr-clone)
("C-c t k" . tmr-cancel)
("C-c t s" . tmr-reschedule)
("C-c t e" . tmr-edit-description)
("C-c t r" . tmr-remove)
("C-c t R" . tmr-remove-finished))
:config
(prot-emacs-configure
(prot-emacs-install tmr)
(define-key global-map (kbd "C-c t") #'tmr-prefix-map)
(setq tmr-sound-file "/usr/share/sounds/freedesktop/stereo/alarm-clock-elapsed.oga"
tmr-notification-urgency 'normal
tmr-description-list 'tmr-description-history))
;;; Pass interface (password-store)
(use-package password-store
:ensure t
(prot-emacs-configure
(prot-emacs-install password-store)
;; Mnemonic is the root of the "code" word (κώδικας). But also to add
;; the password to the kill-ring. Other options are already taken.
:bind ("C-c k" . password-store-copy)
:config
(setq password-store-time-before-clipboard-restore 30))
(define-key global-map (kbd "C-c k") #'password-store-copy)
(use-package pass
:ensure t
:commands (pass))
(setq password-store-time-before-clipboard-restore 30)
;;; Shell (M-x shell)
(use-package shell
:ensure nil
:bind
( :map global-map
("<f1>" . shell) ; I don't use F1 for help commands
:map shell-mode-map
("C-c C-k" . comint-clear-buffer)
("C-c C-w" . comint-write-output))
:config
;; Check my .bashrc which handles `comint-terminfo-terminal':
;;
;; # Default pager. The check for the terminal is useful for Emacs with
;; # M-x shell (which is how I usually interact with bash these days).
;; #
;; # The COLORTERM is documented in (info "(emacs) General Variables").
;; # I found the reference to `dumb-emacs-ansi' in (info "(emacs)
;; # Connection Variables").
;; if [ "$TERM" = "dumb" ] && [ "$INSIDE_EMACS" ]
;; then
;; export PAGER="cat"
;; alias less="cat"
;; export TERM=dumb-emacs-ansi
;; export COLORTERM=1
;; else
;; # Quit once you try to scroll past the end of the file.
;; export PAGER="less --quit-at-eof"
;; fi
(prot-emacs-install pass))
(setq shell-command-prompt-show-cwd t) ; Emacs 27.1
(setq ansi-color-for-comint-mode t)
(setq shell-input-autoexpand 'input)
(setq shell-highlight-undef-enable t) ; Emacs 29.1
(setq shell-has-auto-cd nil) ; Emacs 29.1
(setq shell-get-old-input-include-continuation-lines t) ; Emacs 30.1
(setq shell-kill-buffer-on-exit t) ; Emacs 29.1
(setq shell-completion-fignore '("~" "#" "%"))
(setq-default comint-scroll-to-bottom-on-input t)
(setq-default comint-scroll-to-bottom-on-output nil)
(setq-default comint-input-autoexpand 'input)
;;; Generic interface for shells or REPLs (comint)
(prot-emacs-configure
;; Support for OS-specific escape sequences such as what `ls
;; --hyperlink' uses. I normally don't use those, but I am checking
;; this to see if there are any obvious advantages/disadvantages.
(add-hook 'comint-output-filter-functions #'comint-osc-process-output)
(setq ansi-color-for-comint-mode t) ; also see `ansi-color-for-compilation-mode'
(setq comint-prompt-read-only t)
(setq comint-buffer-maximum-size 9999)
(setq comint-completion-autolist t)
(setq comint-input-ignoredups t)
(setq tramp-default-remote-shell "/bin/bash")
(setq-default comint-scroll-to-bottom-on-input t)
(setq-default comint-scroll-to-bottom-on-output nil)
(setq-default comint-input-autoexpand 'input))
(setq shell-font-lock-keywords
'(("[ \t]\\([+-][^ \t\n]+\\)" 1 font-lock-builtin-face)
("^[^ \t\n]+:.*" . font-lock-string-face)
("^\\[[1-9][0-9]*\\]" . font-lock-constant-face)))
;;; Compilation interface (M-x compile)
(prot-emacs-configure
(add-hook 'compilation-filter-hook #'ansi-color-compilation-filter)
(setq ansi-color-for-compilation-mode t)) ; also see `ansi-color-for-comint-mode'
;; Support for OS-specific escape sequences such as what `ls
;; --hyperlink' uses. I normally don't use those, but I am checking
;; this to see if there are any obvious advantages/disadvantages.
(add-hook 'comint-output-filter-functions 'comint-osc-process-output))
;;; Standard Unix Shell (M-x shell)
(prot-emacs-configure
(prot-emacs-autoload (prot-shell prot-shell-mode) "prot-shell")
(use-package prot-shell
:ensure nil
:hook (shell-mode . prot-shell-mode))
(define-key global-map (kbd "<f1>") #'prot-shell) ; I don't use F1 for help commands
;;; Laptop settings
(unless (directory-empty-p "/sys/class/power_supply/")
(add-to-list 'default-frame-alist '(width . (text-pixels . 800)))
(add-to-list 'default-frame-alist '(height . (text-pixels . 600)))
(with-eval-after-load 'prot-shell
(add-hook 'shell-mode-hook #'prot-shell-mode)
(use-package battery
:ensure nil
:hook (after-init . display-battery-mode)
:config
;;;; Show battery status on the mode line (battery.el)
(prot-emacs-keybind shell-mode-map
"C-c C-k" #'comint-clear-buffer
"C-c C-w" #'comint-write-output)
;; Check my .bashrc which handles `comint-terminfo-terminal':
;;
;; # Default pager. The check for the terminal is useful for Emacs with
;; # M-x shell (which is how I usually interact with bash these days).
;; #
;; # The COLORTERM is documented in (info "(emacs) General Variables").
;; # I found the reference to `dumb-emacs-ansi' in (info "(emacs)
;; # Connection Variables").
;; if [ "$TERM" = "dumb" ] && [ "$INSIDE_EMACS" ] || [ "$TERM" = "dumb-emacs-ansi" ] && [ "$INSIDE_EMACS" ]
;; then
;; export PAGER="cat"
;; alias less="cat"
;; export TERM=dumb-emacs-ansi
;; export COLORTERM=1
;; else
;; # Quit once you try to scroll past the end of the file.
;; export PAGER="less --quit-at-eof"
;; fi
(setq shell-command-prompt-show-cwd t) ; Emacs 27.1
(setq shell-input-autoexpand 'input)
(setq shell-highlight-undef-enable t) ; Emacs 29.1
(setq shell-has-auto-cd nil) ; Emacs 29.1
(setq shell-get-old-input-include-continuation-lines t) ; Emacs 30.1
(setq shell-kill-buffer-on-exit t) ; Emacs 29.1
(setq shell-completion-fignore '("~" "#" "%"))
(setq tramp-default-remote-shell "/bin/bash")
(setq shell-font-lock-keywords
'(("[ \t]\\([+-][^ \t\n]+\\)" 1 font-lock-builtin-face)
("^[^ \t\n]+:.*" . font-lock-string-face)
("^\\[[1-9][0-9]*\\]" . font-lock-constant-face)))))
;;; Show battery status on the mode line with `display-battery-mode'
(when prot-laptop-p
(prot-emacs-configure
(require 'battery)
(setq battery-mode-line-format
(cond
((eq battery-status-function #'battery-linux-proc-acpi)
"⏻%b%p%%,%d°C ")
(battery-status-function
"⏻%b%p%% ")))))
"⏻%b%p%% ")))
(display-battery-mode 1)))
(provide 'prot-emacs-essentials)

View file

@ -1,215 +1,312 @@
;;;; `ediff'
(use-package ediff
:ensure nil
:commands (ediff-buffers ediff-files ediff-buffers3 ediff-files3)
:init
(setq ediff-split-window-function 'split-window-horizontally)
(setq ediff-window-setup-function 'ediff-setup-windows-plain)
:config
(setq ediff-keep-variants nil)
(setq ediff-make-buffers-readonly-at-startup nil)
(setq ediff-merge-revisions-with-ancestor t)
(setq ediff-show-clashes-only t))
;;;; `project'
(use-package project
:ensure nil
:bind
(("C-x p ." . project-dired)
("C-x p C-g" . keyboard-quit)
("C-x p <return>" . project-dired)
("C-x p <delete>" . project-forget-project))
:config
(prot-emacs-configure
(prot-emacs-keybind global-map
"C-x p ." #'project-dired
"C-x p C-g" #'keyboard-quit
"C-x p <return>" #'project-dired
"C-x p <delete>" #'project-forget-project)
(setopt project-switch-commands
'((project-find-file "Find file")
(project-find-regexp "Find regexp")
(project-find-dir "Find directory")
(project-find-regexp "Find regexp")
(project-dired "Root dired")
(project-vc-dir "VC-Dir")
(project-shell "Shell")
(project-any-command "Other command")
(keyboard-quit "Quit")))
(setq project-vc-extra-root-markers '(".project")) ; Emacs 29
(setq project-key-prompt-style t) ; Emacs 30
(advice-add #'project-switch-project :after #'prot-common-clear-minibuffer-message))
(advice-add #'project-switch-project :after #'prot-common-clear-minibuffer-message)
(use-package prot-project
:ensure nil
;; Also check the command `prot-project-in-tab'. I do not use it
;; because I prefer to manage my buffers in frames, with my
;; `beframe' package.
:bind
( :map project-prefix-map
("p" . prot-project-switch)))
(autoload #'prot-project-maybe-in-tab "prot-project")
(autoload #'prot-project-switch "prot-project")
(define-key project-prefix-map (kbd "p") #'prot-project-maybe-in-tab))
;;;; `diff-mode'
(use-package diff-mode
:ensure nil
:defer t
:config
(prot-emacs-configure
(setq diff-default-read-only t)
(setq diff-advance-after-apply-hunk t)
(setq diff-update-on-the-fly t)
;; The following are from Emacs 27.1
(setq diff-refine nil) ; I do it on demand, with my `agitate' package (more below)
(setq diff-font-lock-prettify t) ; I think nil is better for patches, but let me try this for a while
(setq diff-font-lock-syntax 'hunk-also))
(setq diff-font-lock-syntax nil))
;;; Version control framework (vc.el, vc-git.el, and more)
(use-package vc
:ensure nil
:bind
(;; NOTE: I override lots of the defaults
:map global-map
("C-x v B" . vc-annotate) ; Blame mnemonic
("C-x v e" . vc-ediff)
("C-x v k" . vc-delete-file) ; 'k' for kill==>delete is more common
("C-x v G" . vc-log-search) ; git log --grep
("C-x v t" . vc-create-tag)
("C-x v d" . vc-diff)
("C-x v ." . vc-dir-root) ; `vc-dir-root' is from Emacs 28
("C-x v <return>" . vc-dir-root)
:map vc-dir-mode-map
("t" . vc-create-tag)
("O" . vc-log-outgoing)
("o" . vc-dir-find-file-other-window)
("d" . vc-diff) ; parallel to D: `vc-root-diff'
("k" . vc-dir-delete-file)
("G" . vc-revert)
:map vc-git-stash-shared-map
("a" . vc-git-stash-apply-at-point)
("c" . vc-git-stash) ; "create" named stash
("k" . vc-git-stash-delete-at-point) ; symmetry with `vc-dir-delete-file'
("p" . vc-git-stash-pop-at-point)
("s" . vc-git-stash-snapshot)
:map vc-annotate-mode-map
("M-q" . vc-annotate-toggle-annotation-visibility)
("C-c C-c" . vc-annotate-goto-line)
("<return>" . vc-annotate-find-revision-at-line)
:map log-edit-mode-map
("M-s" . nil) ; I use M-s for my search commands
("M-r" . nil) ; I use `consult-history'
:map log-view-mode-map
("<tab>" . log-view-toggle-entry-display)
("<return>" . log-view-find-revision)
("s" . vc-log-search)
("o" . vc-log-outgoing)
("f" . vc-log-incoming)
("F" . vc-update)
("P" . vc-push))
:init
;;;; `diff-hl'
(prot-emacs-configure
(prot-emacs-install diff-hl)
(setq diff-hl-draw-borders nil)
(setq diff-hl-update-async t)
(prot-emacs-keybind global-map
"C-x v <up>" #'diff-hl-previous-hunk
"C-x v <down>" #'diff-hl-next-hunk)
(prot-emacs-hook (prog-mode-hook vc-dir-mode-hook) turn-on-diff-hl-mode)
(with-eval-after-load 'magit
(add-hook 'magit-post-refresh-hook #'diff-hl-magit-post-refresh)))
;;;; `ediff'
(prot-emacs-configure
(setq ediff-split-window-function 'split-window-horizontally)
(setq ediff-window-setup-function 'ediff-setup-windows-plain)
(setq ediff-keep-variants nil)
(setq ediff-make-buffers-readonly-at-startup nil)
(setq ediff-merge-revisions-with-ancestor t)
(setq ediff-show-clashes-only t)
(with-eval-after-load 'outline
(add-hook 'ediff-prepare-buffer-hook #'outline-show-all))
(prot-emacs-autoload
(prot-ediff-visible-buffers-2
prot-ediff-visible-buffers-3
prot-ediff-store-layout
prot-ediff-restore-layout)
"prot-ediff")
;; The C-x v prefix is for all "version control" commands that are
;; already built into Emacs. It makes sense to extend it for this
;; use-case.
(prot-emacs-keybind global-map
"C-x v 2" #'prot-ediff-visible-buffers-2
"C-x v 3" #'prot-ediff-visible-buffers-3)
(add-hook 'ediff-before-setup-hook #'prot-ediff-store-layout)
(add-hook 'ediff-quit-hook #'prot-ediff-restore-layout))
;;;; `smerge-mode'
(prot-emacs-configure
(setq smerge-diff-buffer-name "*smerge-diff*")
(setq smerge-refine-shadow-cursor nil)) ; Emacs 31
;;; Version control framework (vc.el, vc-git.el, and more)
(prot-emacs-configure
(setq vc-follow-symlinks t)
:config
;; Those offer various types of functionality, such as blaming,
;; viewing logs, showing a dedicated buffer with changes to affected
;; files.
(require 'vc-annotate)
(require 'vc-dir)
(require 'vc-git)
(require 'add-log)
(require 'log-view)
;; I only use Git. If I ever need another, I will include it here.
;; This may have an effect on performance, as Emacs will not try to
;; check for a bunch of backends.
(setq vc-handled-backends '(Git))
(with-eval-after-load 'vc
;; Those offer various types of functionality, such as blaming,
;; viewing logs, showing a dedicated buffer with changes to affected
;; files.
(require 'vc-annotate)
(require 'vc-dir)
(require 'vc-git)
(require 'add-log)
(require 'log-view)
;; This one is for editing commit messages.
(require 'log-edit)
(setq log-edit-confirm 'changed)
(setq log-edit-keep-buffer nil)
(setq log-edit-require-final-newline t)
(setq log-edit-setup-add-author nil)
;; I can see the files from the Diff with C-c C-d
(remove-hook 'log-edit-hook #'log-edit-show-files)
;; I only use Git. If I ever need another, I will include it here.
;; This may have an effect on performance, as Emacs will not try to
;; check for a bunch of backends.
(setq vc-handled-backends '(Git))
(setq vc-find-revision-no-save t)
(setq vc-annotate-display-mode 'scale) ; scale to oldest
;; I use a different account for git commits
(setq add-log-mailing-address "info@protesilaos.com")
(setq add-log-keep-changes-together t)
(setq vc-git-diff-switches '("--patch-with-stat" "--histogram"))
(setq vc-git-log-switches '("--stat"))
(setq vc-git-print-log-follow t)
(setq vc-git-revision-complete-only-branches nil) ; Emacs 28
(setq vc-git-root-log-format
`("%d %h %ai %an: %s"
;; The first shy group matches the characters drawn by --graph.
;; We use numbered groups because `log-view-message-re' wants the
;; revision number to be group 1.
,(concat "^\\(?:[*/\\|]+\\)\\(?:[*/\\| ]+\\)?"
"\\(?2: ([^)]+) \\)?\\(?1:[0-9a-z]+\\) "
"\\(?4:[0-9]\\{4\\}-[0-9-]\\{4\\}[0-9\s+:-]\\{16\\}\\) "
"\\(?3:.*?\\):")
((1 'log-view-message)
(2 'change-log-list nil lax)
(3 'change-log-name)
(4 'change-log-date))))
(setq vc-dir-save-some-buffers-on-revert t) ; Emacs 31
(setq vc-use-incoming-outgoing-prefixes t) ; Emacs 31
(setq vc-async-checkin t) ; Emacs 31
(setq vc-display-failed-async-commands t) ; Emacs 31
(setq vc-dir-show-key-binding-hints nil) ; Emacs 32
;; These two are from Emacs 29
(setq vc-git-log-edit-summary-target-len 50)
(setq vc-git-log-edit-summary-max-len 70))
;; This one is for editing commit messages.
(require 'log-edit)
(setq log-edit-confirm 'changed)
(setq log-edit-keep-buffer nil)
(setq log-edit-require-final-newline t)
(setq log-edit-setup-add-author nil)
;;; Agitate
;; A package of mine to complement VC and friends. Read the manual
;; here: <https://protesilaos.com/emacs/agitate>.
(use-package agitate
:ensure t
:after vc
:hook (diff-mode . agitate-diff-enable-outline-minor-mode)
:bind
:map global-map
(("C-x v =" . agitate-diff-buffer-or-file) ; replace `vc-diff'
("C-x v g" . agitate-vc-git-grep) ; replace `vc-annotate'
("C-x v f" . agitate-vc-git-find-revision)
("C-x v s" . agitate-vc-git-show)
("C-x v w" . agitate-vc-git-kill-commit-message)
("C-x v p p" . agitate-vc-git-format-patch-single)
("C-x v p n" . agitate-vc-git-format-patch-n-from-head)
:map diff-mode-map
("C-c C-b" . agitate-diff-refine-cycle) ; replace `diff-refine-hunk'
("C-c C-n" . agitate-diff-narrow-dwim)
("L" . vc-print-root-log)
;; Emacs 29 can use C-x v v in diff buffers, which is great, but now I
;; need quick access to it...
("v" . vc-next-action)
:map log-view-mode-map
("w" . agitate-log-view-kill-revision)
("W" . agitate-log-view-kill-revision-expanded)
:map vc-git-log-view-mode-map
("c" . agitate-vc-git-format-patch-single)
:map log-edit-mode-map
("C-c C-i C-n" . agitate-log-edit-insert-file-name)
;; See user options `agitate-log-edit-emoji-collection' and
;; `agitate-log-edit-conventional-commits-collection'.
("C-c C-i C-e" . agitate-log-edit-emoji-commit)
("C-c C-i C-c" . agitate-log-edit-conventional-commit))
:config
(advice-add #'vc-git-push :override #'agitate-vc-git-push-prompt-for-remote)
(prot-emacs-hook
log-edit-hook
(log-edit-insert-message-template
log-edit-maybe-show-diff)
nil
log-edit)
(setq agitate-log-edit-informative-show-root-log nil
agitate-log-edit-informative-show-files nil)
(setq vc-find-revision-no-save t)
(setq vc-annotate-display-mode 'scale) ; scale to oldest
;; I use a different account for git commits
(setq add-log-mailing-address "info@protesilaos.com")
(setq add-log-keep-changes-together t)
(setq vc-git-diff-switches '("--patch-with-stat" "--histogram"))
(setq vc-git-log-switches '("--stat"))
(setq vc-git-print-log-follow t)
(setq vc-git-revision-complete-only-branches nil) ; Emacs 28
(setq vc-git-root-log-format
`("%d %h %ai %an: %s"
;; The first shy group matches the characters drawn by --graph.
;; We use numbered groups because `log-view-message-re' wants the
;; revision number to be group 1.
,(concat "^\\(?:[*/\\|]+\\)\\(?:[*/\\| ]+\\)?"
"\\(?2: ([^)]+) \\)?\\(?1:[0-9a-z]+\\) "
"\\(?4:[0-9]\\{4\\}-[0-9-]\\{4\\}[0-9\s+:-]\\{16\\}\\) "
"\\(?3:.*?\\):")
((1 'log-view-message)
(2 'change-log-list nil lax)
(3 'change-log-name)
(4 'change-log-date))))
(agitate-log-edit-informative-mode 1))
;; These two are from Emacs 29
(setq vc-git-log-edit-summary-target-len 50)
(setq vc-git-log-edit-summary-max-len 70)
(define-advice vc-push (:around (&rest args) prot)
(let ((current-window (selected-window)))
(apply args)
(select-window current-window)))
(define-advice vc-pull (:around (&rest args) prot)
(let ((current-window (selected-window)))
(apply args)
(select-window current-window)))
(defun prot/vc-diff-dwim ()
"Show diff of buffer against file or against VC history."
(interactive)
(if-let* ((buffer (current-buffer))
(_ (buffer-modified-p buffer)))
(diff-buffer-with-file buffer)
(call-interactively #'vc-diff)))
(defvar prot/vc-git-grep-history nil
"Minibuffer history for `prot/vc-git-grep'.")
(defun prot/vc-git-grep (directory regexp)
"Use `vc-git-grep' with REGEXP in the current root Git DIRECTORY."
(interactive
(let ((directory (or (vc-root-dir)
(locate-dominating-file "." ".git")
(user-error "No VC root available"))))
(list
directory
(read-regexp
(format "vc-git-grep for REGEXP in `%s': "
(propertize directory 'face 'warning))
nil 'prot/vc-git-grep-history))))
(vc-git-grep regexp "*" directory))
;; NOTE: I override lots of the defaults
(prot-emacs-keybind global-map
"C-x v B" #'vc-annotate ; Blame mnemonic
"C-x v g" #'prot/vc-git-grep ; override original `vc-annotate' key
"C-x v e" #'vc-ediff
"C-x v k" #'vc-delete-file ; 'k' for kill==>delete is more common
"C-x v G" #'vc-log-search ; git log --grep
"C-x v t" #'vc-create-tag
"C-x v c" #'vc-clone ; Emacs 31
"C-x v d" #'prot/vc-diff-dwim
"C-x v ." #'vc-dir-root ; `vc-dir-root' is from Emacs 28
"C-x v <return>" #'vc-dir-root)
(prot-emacs-keybind vc-dir-mode-map
"t" #'vc-create-tag
"I" #'vc-log-incoming
"O" #'vc-log-outgoing
"o" #'vc-dir-find-file-other-window
"d" #'vc-diff ; parallel to D: `vc-root-diff'
"k" #'vc-dir-delete-file
"G" #'vc-revert)
(prot-emacs-keybind vc-git-stash-shared-map
"a" #'vc-git-stash-apply-at-point
"c" #'vc-git-stash ; "create" named stash
"k" #'vc-git-stash-delete-at-point ; symmetry with `vc-dir-delete-file'
"p" #'vc-git-stash-pop-at-point
"s" #'vc-git-stash-snapshot)
(prot-emacs-keybind vc-annotate-mode-map
"M-q" #'vc-annotate-toggle-annotation-visibility
"C-c C-c" #'vc-annotate-goto-line
"<return>" #'vc-annotate-find-revision-at-line)
(prot-emacs-keybind log-edit-mode-map
"M-s" nil ; I use M-s for my search commands
"M-r" nil)
(prot-emacs-keybind log-view-mode-map
"<tab>" #'log-view-toggle-entry-display
"<return>" #'log-view-find-revision
"s" #'vc-log-search))
(defun prot/modus-vc-annotate ()
(modus-themes-with-colors
(setq vc-annotate-background-mode nil)
(setq vc-annotate-very-old-color fg-dim)
(setq vc-annotate-color-map
`(( 20. . ,red)
( 40. . ,red-cooler)
( 60. . ,red-warmer)
( 80. . ,yellow-warmer)
(100. . ,yellow)
(120. . ,yellow-cooler)
(140. . ,green-warmer)
(160. . ,green)
(180. . ,green-cooler)
(200. . ,cyan-cooler)
(220. . ,cyan-warmer)
(240. . ,cyan)
(260. . ,blue-warmer)
(280. . ,blue)
(300. . ,blue-cooler)
(320. . ,blue-intense)
(340. . ,magenta-cooler)
(360. . ,fg-dim)))))
(when (memq prot-emacs-load-theme-family '(modus ef standard))
(with-eval-after-load 'vc-annotate
(prot/modus-vc-annotate)
(add-hook 'modus-themes-after-load-theme-hook #'prot/modus-vc-annotate))))
;;; Interactive and powerful git front-end (Magit)
(use-package magit
:ensure t
:bind ("C-c g" . magit-status)
:init
(prot-emacs-configure
(setq transient-show-popup 0.5)
(prot-emacs-install magit)
;; Let `display-buffer-alist' do its job
(setq magit-display-buffer-function #'display-buffer)
(define-key global-map (kbd "C-c g") #'magit-status)
(with-eval-after-load 'magit
(prot-emacs-keybind magit-mode-map
"C-w" #'nil
"M-w" #'nil))
(setq magit-define-global-key-bindings nil)
(setq magit-section-visibility-indicator '(""))
:config
(setq magit-section-visibility-indicators
`(;; (magit-fringe-bitmap> . magit-fringe-bitmapv)
("" . t))) ; same as `org-ellipsis'
;; Show icons for files in the Magit status and other buffers. This
;; is from my prot-icons.el.
(with-eval-after-load 'prot-icons
(setq magit-format-file-function
(lambda (_kind file face &rest _)
(let ((icon (prot-icons-get-file-icon file)))
(format "%s %s" icon (propertize file 'font-lock-face face))))))
(setq magit-diff-refine-hunk t)
(setq magit-diff-fontify-hunk nil)
(setq magit-diff-refine-ignore-whitespace t)
(setq magit-log-auto-more t)
(setq magit-repository-directories
'(("~/Git/Projects" . 1)))
(setq magit-repolist-columns
`(("Name" 25 ,#'magit-repolist-column-ident)
("Version" 15 ,#'magit-repolist-column-version
((:sort magit-repolist-version<)))
("Unpulled" 10 ,#'magit-repolist-column-unpulled-from-upstream
((:help-echo "Upstream changes not in branch")
(:right-align t)
(:sort <)))
("Unpushed" 10 ,#'magit-repolist-column-unpushed-to-upstream
((:help-echo "Local changes not in upstream")
(:right-align t)
(:sort <)))
("Path" 99 ,#'magit-repolist-column-path)))
(setq git-commit-summary-max-length 50)
;; NOTE 2023-01-24: I used to also include `overlong-summary-line'
;; in this list, but I realised I do not need it. My summaries are
;; always in check. When I exceed the limit, it is for a good
;; reason.
(setq git-commit-style-convention-checks '(non-empty-second-line))
(setq magit-diff-refine-hunk t)
(setq magit-repository-directories
'(("~/Git/Projects" . 1))))
(setq git-commit-major-mode #'text-mode))
(provide 'prot-emacs-git)

View file

@ -1,6 +1,6 @@
;; This is deprecated code as I stopped using Gnus a long time ago. I
;; keep it here for when I need to test it for my themes (`ef-themes',
;; `modus-themes', `standard-themes').
;; `modus-themes', `standard-themes', `doric-themes').
(use-package gnus
@ -129,7 +129,7 @@
;; name, this will use the contents of the "To:" field, prefixed by
;; the string I specify. Useful when checking your "Sent" summary or
;; a mailing list you participate in.
(setq gnus-ignored-from-addresses "Protesilaos Stavrou")
(setq gnus-ignored-from-addresses "Protesilaos")
(setq gnus-summary-to-prefix "To: ")
(setq gnus-summary-line-format "%U%R %-18,18&user-date; %4L:%-25,25f %B%s\n")

View file

@ -1,23 +1,46 @@
;;; Icons
(use-package nerd-icons
:ensure t)
;; NOTE 2025-12-21: Also see the `prot-minibuffer.el' and my
;; `completion-category-overrides' for completion-related icons.
(prot-emacs-configure
(require 'prot-icons)
(use-package nerd-icons-completion
:ensure t
:after marginalia
:config
(nerd-icons-completion-marginalia-setup)
(nerd-icons-completion-mode 1))
(setq prot-icons-style 'emoji) ; `alphabetic', `symbolic', or `emoji'
(use-package nerd-icons-corfu
:ensure t
:after corfu
:config
(add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter))
(prot-icons-dired-mode 1)
(prot-icons-xref-mode 1)
(use-package nerd-icons-dired
:ensure t
:hook
(dired-mode . nerd-icons-dired-mode))
(when (and prot-emacs-load-theme-family
(eq prot-icons-style 'alphabetic))
(cond
((memq prot-emacs-load-theme-family '(modus ef standard))
(defun prot/icons-set-faces ()
(modus-themes-with-colors
(custom-set-faces
`(prot-icons-icon ((,c :inherit (bold fixed-pitch) :box (:line-width (1 . -1)) :inverse-video t)))
`(prot-icons-directory ((,c :inherit bold :foreground ,accent-0)))
`(prot-icons-gray ((,c :inherit bold :foreground ,fg-dim)))
`(prot-icons-red ((,c :inherit prot-icons-icon :foreground ,red)))
`(prot-icons-green ((,c :inherit prot-icons-icon :foreground ,green-warmer)))
`(prot-icons-yellow ((,c :inherit prot-icons-icon :foreground ,yellow)))
`(prot-icons-blue ((,c :inherit prot-icons-icon :foreground ,blue-cooler)))
`(prot-icons-magenta ((,c :inherit prot-icons-icon :foreground ,magenta-cooler)))
`(prot-icons-cyan ((,c :inherit prot-icons-icon :foreground ,cyan))))))
(add-hook 'modus-themes-after-load-theme-hook #'prot/icons-set-faces))
((eq prot-emacs-load-theme-family 'doric)
(defun prot/icons-set-faces ()
(doric-themes-with-colors
(custom-set-faces
`(prot-icons-icon ((t :inherit (bold fixed-pitch) :box (:line-width (1 . -1)) :inverse-video t)))
`(prot-icons-directory ((t :inherit bold :foreground ,fg-accent)))
`(prot-icons-gray ((t :inherit bold :foreground ,fg-shadow-subtle)))
`(prot-icons-red ((t :inherit prot-icons-icon :foreground ,fg-red)))
`(prot-icons-green ((t :inherit prot-icons-icon :foreground ,fg-green)))
`(prot-icons-yellow ((t :inherit prot-icons-icon :foreground ,fg-yellow)))
`(prot-icons-blue ((t :inherit prot-icons-icon :foreground ,fg-blue)))
`(prot-icons-magenta ((t :inherit prot-icons-icon :foreground ,fg-magenta)))
`(prot-icons-cyan ((t :inherit prot-icons-icon :foreground ,fg-cyan))))))
(add-hook 'doric-themes-after-load-theme-hook #'prot/icons-set-faces)))
(prot/icons-set-faces)))
(provide 'prot-emacs-icons)

View file

@ -1,379 +1,400 @@
;;;; Tabs, indentation, and the TAB key
(use-package emacs
:ensure nil
:demand t
:config
(prot-emacs-configure
(setq tab-always-indent 'complete)
(setq tab-first-completion 'word-or-paren-or-punct) ; Emacs 27
(setq tab-first-completion nil) ; Emacs 27
(setq-default tab-width 4
indent-tabs-mode nil))
;;;; Disable "electric" behaviour
(use-package electric
:ensure nil
:hook
(prog-mode . electric-indent-local-mode)
:config
;; I don't like auto indents in Org and related. They are okay for
;; programming.
(electric-pair-mode -1)
;;;; Emacs Lisp major mode
(prot-emacs-configure
(with-eval-after-load 'elisp-mode
(prot-emacs-keybind emacs-lisp-mode-map
"C-c C-b" nil ; I do not want to byte compile the buffer
"C-c C-f" nil) ; .. nor the file
(prot-emacs-keybind lisp-interaction-mode-map
"C-c C-b" nil
"C-c C-f" nil)
;; All of these are from Emacs 31.
(setq elisp-eldoc-funcall-with-docstring-length 'short)
(setq elisp-eldoc-docstring-length-limit 1000)
(set-default-toplevel-value 'lexical-binding t) ; Emacs 31
(dolist (package prot-emacs-my-packages)
(add-to-list 'elisp-flymake-byte-compile-load-path (expand-file-name (format "%s" package) "/home/prot/Git/Projects/")))
(require 'prot-elisp)
(prot-emacs-keybind emacs-lisp-mode-map
"C-j" #'prot-elisp-eval-and-print-last-sexp ; overrides `electric-newline-and-maybe-indent'
"C-c C-p" #'prot-elisp-pp-macroexpand-last-sexp
"C-M-w" #'prot-elisp-copy-current-definition) ; overrides `append-next-kill'
(prot-emacs-keybind lisp-interaction-mode-map
"C-j" #'prot-elisp-eval-and-print-last-sexp ; overrides `eval-print-last-sexp'
"C-c C-p" #'prot-elisp-pp-macroexpand-last-sexp
"C-M-w" #'prot-elisp-copy-current-definition))) ; overrides `append-next-kill'
;;;; The "electric" behaviour
(prot-emacs-configure
(electric-pair-mode 1)
(electric-indent-mode 1)
(electric-quote-mode -1)
(electric-indent-mode -1))
(defun prot/electric-indent-mode-disable ()
"Disable `electric-indent-local-mode'."
(electric-indent-local-mode -1))
(add-hook 'org-mode-hook #'prot/electric-indent-mode-disable))
;;;; Parentheses (show-paren-mode)
(use-package paren
:ensure nil
:hook (prog-mode . show-paren-local-mode)
:config
(setq show-paren-style 'parenthesis)
(setq show-paren-when-point-in-periphery nil)
(setq show-paren-when-point-inside-paren nil)
(setq show-paren-context-when-offscreen 'overlay)) ; Emacs 29
(prot-emacs-configure
(add-hook 'prog-mode-hook #'show-paren-local-mode)
(with-eval-after-load 'paren
(setq show-paren-style 'parenthesis)
(setq show-paren-when-point-in-periphery nil)
(setq show-paren-when-point-inside-paren nil)
(setq show-paren-context-when-offscreen 'overlay) ; Emacs 29
(setq show-paren-not-in-comments-or-strings nil))) ; Emacs 31
;;;; Plain text (text-mode)
(use-package text-mode
:ensure nil
:mode "\\`\\(README\\|CHANGELOG\\|COPYING\\|LICENSE\\)\\'"
:hook
((text-mode . turn-on-auto-fill)
(prog-mode . (lambda () (setq-local sentence-end-double-space t))))
:config
(setq sentence-end-double-space nil)
(setq sentence-end-without-period nil)
(setq colon-double-space nil)
(setq use-hard-newlines nil)
(setq adaptive-fill-mode t))
(prot-emacs-configure
(add-to-list 'auto-mode-alist '("\\`\\(README\\|CHANGELOG\\|COPYING\\|LICENSE\\)\\'" . text-mode))
;;;; Arch Linux and AUR package scripts (sh-mode)
(use-package sh-script
:ensure nil
:mode ("PKGBUILD" . sh-mode))
(add-hook 'text-mode-hook #'turn-on-auto-fill)
(add-hook 'prog-mode-hook (lambda () (setq-local sentence-end-double-space t)))
;;;; SystemD and other configuration files (conf-mode)
(use-package conf-mode
:ensure nil
:mode ("\\`dircolors\\'" "\\.\\(service\\|timer\\)\\'"))
(with-eval-after-load 'text-mode
(setq sentence-end-double-space nil)
(setq sentence-end-without-period nil)
(setq colon-double-space nil)
(setq use-hard-newlines nil)
(setq adaptive-fill-mode t)))
(prot-emacs-configure
;; Arch Linux and AUR package scripts (sh-mode)
(add-to-list 'auto-mode-alist '("PKGBUILD" . sh-mode))
;; SystemD and other configuration files (conf-mode)
(add-to-list 'auto-mode-alist '("\\`dircolors\\'" "\\.\\(service\\|timer\\)\\'" "dunstrc")))
;;;; Eldoc (Emacs live documentation feedback)
(use-package eldoc
:ensure nil
:hook (prog-mode . eldoc-mode)
:config
(prot-emacs-configure
(add-hook 'prog-mode-hook #'eldoc-mode)
(setq eldoc-idle-delay 1.0)
(setq eldoc-message-function #'message)) ; don't use mode line for M-x eval-expression, etc.
;;;; Eglot (built-in client for the language server protocol)
(use-package eglot
:ensure nil
:functions (eglot-ensure)
:commands (eglot)
:config
(prot-emacs-configure
(setq eglot-sync-connect nil)
(setq eglot-autoshutdown t))
;;;; Handle performance for very long lines (so-long.el)
(use-package so-long
:ensure nil
:hook (after-init . global-so-long-mode))
(prot-emacs-configure
(global-so-long-mode 1))
;;; Markdown (markdown-mode)
(use-package markdown-mode
:ensure t
:defer t
:config
(prot-emacs-configure
(prot-emacs-install markdown-mode)
(setq markdown-fontify-code-blocks-natively t))
;;; csv-mode
(prot-emacs-configure
(prot-emacs-install csv-mode))
;;; SXHKDRC mode (one of my many packages)
(use-package sxhkdrc-mode
:ensure t
(prot-emacs-configure
(prot-emacs-install sxhkdrc-mode)
;; By default, it only applies to the sxhkdrc file, but I have other
;; relevant entries as well. I separate my keys into different
;; modules and load only what I need.
:mode "sxhkdrc_.*")
(add-to-list 'auto-mode-alist '("sxhkdrc_.*" . sxhkdrc-mode)))
;;; Flyspell and prot-spell.el (spell check)
(use-package flyspell
:ensure nil
:bind
( :map flyspell-mode-map
("C-;" . nil)
:map ctl-x-x-map
("s" . flyspell-mode)) ; C-x x s
:config
(setq flyspell-issue-message-flag nil)
(setq flyspell-issue-welcome-flag nil)
(setq ispell-program-name "aspell")
(setq ispell-dictionary "en_GB"))
(prot-emacs-configure
(prot-emacs-autoload
(prot-spell-spell-dwim
prot-spell-change-dictionary
prot-spell-spell-dwim
prot-spell-change-dictionary
prot-spell-french-punctuation)
"prot-spell")
(use-package prot-spell
:ensure nil
:bind
(("M-$" . prot-spell-spell-dwim)
("C-M-$" . prot-spell-change-dictionary))
:config
(setq prot-spell-dictionaries
'(("EN English" . "en")
("EL Ελληνικά" . "el")
("FR Français" . "fr")
("ES Espanõl" . "es")))
(prot-emacs-keybind global-map
"M-$" #'prot-spell-spell-dwim
"C-M-$" #'prot-spell-change-dictionary
"M-i" #'prot-spell-spell-dwim ; override `tab-to-tab-stop
"C-M-i" #'prot-spell-change-dictionary) ; override `complete-symbol'
;; Also check prot-spell.el for what I am doing with
;; `prot-spell-ispell-display-buffer'. Then refer to the
;; `display-buffer-alist' for the relevant entry.
(setq ispell-choices-buffer "*ispell-top-choices*"))
(with-eval-after-load 'flyspell
(define-key flyspell-mode-map (kbd "C-;") nil)
(define-key flyspell-mouse-map (kbd "<mouse-3>") #'flyspell-correct-word)
(define-key ctl-x-x-map (kbd "s") #'flyspell-mode) ; C-x x s
(setq flyspell-issue-message-flag nil)
(setq flyspell-issue-welcome-flag nil)
(setq ispell-program-name "aspell")
(setq ispell-dictionary "en_GB")
(setq prot-spell-dictionaries
'(("EN English" . "en")
("EL Ελληνικά" . "el")
("FR Français" . "fr")
("ES Espanõl" . "es")))
;; Also check prot-spell.el for what I am doing with
;; `prot-spell-ispell-display-buffer'. Then refer to the
;; `display-buffer-alist' for the relevant entry.
(setq ispell-choices-buffer "*ispell-top-choices*")))
;;; Flymake
(use-package flymake
:ensure nil
:preface
(defvar prot/flymake-mode-projects-path
(file-name-as-directory (expand-file-name "Projects" "~/Git/"))
"Path to my Git projects.")
(defun prot/flymake-mode-lexical-binding ()
(when lexical-binding
(prot-emacs-configure
(defun prot/flymake-mode-in-my-projects ()
"Enable `flymake-mode' when editing a trusted file."
(when (and buffer-file-name
(not (file-directory-p buffer-file-name))
(file-regular-p buffer-file-name)
(seq-some
(lambda (directory-or-file)
(string-prefix-p (expand-file-name directory-or-file) (expand-file-name buffer-file-name)))
trusted-content))
(flymake-mode 1)))
(defun prot/flymake-mode-in-my-projects ()
(when-let ((file (buffer-file-name))
((string-prefix-p prot/flymake-mode-projects-path
(expand-file-name file)))
((not (file-directory-p file)))
((file-regular-p file)))
(add-hook 'find-file-hook #'prot/flymake-mode-lexical-binding nil t)))
(add-hook 'prog-mode-hook #'prot/flymake-mode-in-my-projects)
(add-hook 'emacs-lisp-mode-hook #'prot/flymake-mode-in-my-projects)
:bind
( :map ctl-x-x-map
("m" . flymake-mode) ; C-x x m
:map flymake-mode-map
("C-c ! s" . flymake-start)
("C-c ! d" . flymake-show-buffer-diagnostics) ; Emacs28
("C-c ! D" . flymake-show-project-diagnostics) ; Emacs28
("C-c ! n" . flymake-goto-next-error)
("C-c ! p" . flymake-goto-prev-error))
:config
(setq flymake-fringe-indicator-position 'left-fringe)
(setq flymake-suppress-zero-counters t)
(setq flymake-no-changes-timeout nil)
(setq flymake-start-on-flymake-mode t)
(setq flymake-start-on-save-buffer t)
(setq flymake-proc-compilation-prevents-syntax-check t)
(setq flymake-wrap-around nil)
(setq flymake-mode-line-format
'("" flymake-mode-line-exception flymake-mode-line-counters))
;; NOTE 2023-07-03: `prot-modeline.el' actually defines the counters
;; itself and ignores this.
(setq flymake-mode-line-counter-format
'("" flymake-mode-line-error-counter
flymake-mode-line-warning-counter
flymake-mode-line-note-counter ""))
(setq flymake-show-diagnostics-at-end-of-line nil)) ; Emacs 30
(define-key ctl-x-x-map (kbd "m") #'flymake-mode) ; C-x x m
(with-eval-after-load 'flymake
(prot-emacs-keybind flymake-mode-map
"C-c ! s" #'flymake-start
"C-c ! d" #'flymake-show-buffer-diagnostics ; Emacs 28
"C-c ! D" #'flymake-show-project-diagnostics ; Emacs 28
"C-c ! n" #'flymake-goto-next-error
"C-c ! p" #'flymake-goto-prev-error)
(setq flymake-fringe-indicator-position 'left-fringe)
(setq flymake-suppress-zero-counters t)
(setq flymake-no-changes-timeout nil)
(setq flymake-start-on-flymake-mode t)
(setq flymake-start-on-save-buffer t)
(setq flymake-proc-compilation-prevents-syntax-check t)
(setq flymake-wrap-around nil)
(setq flymake-mode-line-format
'("" flymake-mode-line-exception flymake-mode-line-counters))
;; NOTE 2023-07-03: `prot-modeline.el' actually defines the counters
;; itself and ignores this.
(setq flymake-mode-line-counter-format
'("" flymake-mode-line-error-counter
flymake-mode-line-warning-counter
flymake-mode-line-note-counter ""))
(setq flymake-inline-diagnostics ; Emacs 32
'((current . fancy)
(current-line . short)
(t . nil))))
;;; Elisp packaging requirements
(use-package package-lint-flymake
:ensure t
:after flymake
:config
(add-hook 'flymake-diagnostic-functions #'package-lint-flymake))
(prot-emacs-install package-lint-flymake)
(package-lint-flymake-setup))
;;; General configurations for prose/writing
;;;; `outline' (`outline-mode' and `outline-minor-mode')
(use-package outline
:ensure nil
:bind
("<f10>" . outline-minor-mode)
:config
(prot-emacs-configure
(define-key global-map (kbd "<f10>") #'outline-minor-mode)
(setq outline-minor-mode-highlight nil) ; emacs28
(setq outline-minor-mode-cycle t) ; emacs28
(setq outline-minor-mode-use-buttons nil) ; emacs29---bless you for the nil option!
(setq outline-minor-mode-use-margins nil)) ; as above
;;;; `dictionary'
(use-package dictionary
:ensure nil
:bind ("C-c d" . dictionary-search)
:config
(setq dictionary-server "dict.org"
dictionary-default-popup-strategy "lev" ; read doc string
dictionary-create-buttons nil
dictionary-use-single-buffer t))
(prot-emacs-configure
(define-key global-map (kbd "C-c d") #'dictionary-search)
(setq dictionary-server "dict.org")
(setq dictionary-default-popup-strategy "lev") ; read doc string
(setq dictionary-create-buttons nil)
(setq dictionary-use-single-buffer t))
;;; aLtCaPs
;; Read the manual: <https://protesilaos.com/emacs/altcaps>.
(use-package altcaps
:ensure t
:bind
("C-x C-a" . altcaps-dwim)
:config
(prot-emacs-configure
(prot-emacs-install altcaps)
(define-key global-map (kbd "C-x C-a") #'altcaps-dwim)
;; Force letter casing for certain characters (for legibility).
(setq altcaps-force-character-casing
'(;; Greek theta
( . downcase))))
;;; Denote (simple note-taking and file-naming)
;; Read the manual: <https://protesilaos.com/emacs/denote>.
(use-package denote
:ensure t
:hook
;; If you use Markdown or plain text files you want to buttonise
;; existing buttons upon visiting the file (Org renders links as
;; buttons right away).
((find-file . denote-link-buttonize-buffer)
;; Highlight Denote file names in Dired buffers. Below is the
;; generic approach, which is great if you rename files Denote-style
;; in lots of places as I do.
;;
;; If you only want the `denote-dired-mode' in select directories,
;; then modify the variable `denote-dired-directories' and use the
;; following instead:
;;
;; (dired-mode . denote-dired-mode-in-directories)
(dired-mode . denote-dired-mode))
:bind
;; Denote DOES NOT define any key bindings. This is for the user to
;; decide. For example:
( :map global-map
("C-c n n" . denote)
("C-c n N" . denote-type)
("C-c n d" . denote-date)
("C-c n z" . denote-signature) ; "zettelkasten" mnemonic
("C-c n s" . denote-subdirectory)
("C-c n o" . denote-sort-dired) ; "order" mnemonic
("C-c n j" . denote-journal-extras-new-entry)
("C-c n J" . denote-journal-extras-new-or-existing-entry)
;; Note that `denote-rename-file' can work from any context, not
;; just Dired buffers. That is why we bind it here to the
;; `global-map'.
;;
;; Also see `denote-rename-file-using-front-matter' further below.
("C-c n r" . denote-rename-file)
;; Read the manual: <https://protesilaos.com/emacs/denote>. This does
;; not include all the useful features of Denote. I have a separate
;; private setup for those, as I need to test everything is in order.
(prot-emacs-configure
(prot-emacs-install denote)
(add-hook 'text-mode-hook #'denote-fontify-links-mode)
(add-hook 'dired-mode-hook #'denote-dired-mode)
;; If you intend to use Denote with a variety of file types, it is
;; easier to bind the link-related commands to the `global-map', as
;; shown here. Otherwise follow the same pattern for
;; `org-mode-map', `markdown-mode-map', and/or `text-mode-map'.
:map text-mode-map
("C-c n i" . denote-link) ; "insert" mnemonic
("C-c n I" . denote-add-links)
("C-c n b" . denote-backlinks)
("C-c n f f" . denote-find-link)
("C-c n f b" . denote-find-backlink)
;; Also see `denote-rename-file' further above.
("C-c n R" . denote-rename-file-using-front-matter)
(prot-emacs-keybind global-map
"C-c n n" #'denote
"C-c n N" #'denote-type
"C-c n d" #'denote-dired
"C-c n g" #'denote-grep
"C-c n r" #'denote-rename-file)
;; I do not bind the Org dynamic blocks, but they are useful:
;;
;; - `denote-org-extras-dblock-insert-links'
;; - `denote-org-extras-dblock-insert-backlinks'
;; - `denote-org-extras-dblock-insert-files'
;; - `denote-org-extras-dblock-insert-missing-links'
(with-eval-after-load 'text-mode
(prot-emacs-keybind text-mode-map
"C-c n i" #'denote-link ; "insert" mnemonic
"C-c n I" #'denote-add-links
"C-c n b" #'denote-backlinks
"C-c n R" #'denote-rename-file-using-front-matter))
;; Key bindings specifically for Dired.
:map dired-mode-map
("C-c C-d C-i" . denote-link-dired-marked-notes)
("C-c C-d C-r" . denote-dired-rename-marked-files)
("C-c C-d C-k" . denote-dired-rename-marked-files-with-keywords)
("C-c C-d C-f" . denote-dired-rename-marked-files-using-front-matter))
:config
;; Remember to check the doc strings of those variables.
(setq denote-directory (expand-file-name "~/Documents/notes/"))
(setq denote-file-type 'text) ; Org is the default, set others here like I do
;; If you want to have a "controlled vocabulary" of keywords,
;; meaning that you only use a predefined set of them, then you want
;; `denote-infer-keywords' to be nil and `denote-known-keywords' to
;; have the keywords you need.
(setq denote-known-keywords '("emacs" "philosophy" "politics" "economics"))
(setq denote-infer-keywords t)
(setq denote-sort-keywords t)
(setq denote-excluded-directories-regexp nil)
(setq denote-date-format nil) ; read its doc string
(setq denote-rename-no-confirm t)
(setq denote-backlinks-show-context nil)
(setq denote-rename-buffer-format "[D] %t")
(with-eval-after-load 'dired
(prot-emacs-keybind dired-mode-map
"C-c C-d C-i" #'denote-dired-link-marked-notes
"C-c C-d C-r" #'denote-dired-rename-marked-files
"C-c C-d C-k" #'denote-dired-rename-marked-files-with-keywords
"C-c C-d C-f" #'denote-dired-rename-marked-files-using-front-matter))
;; Automatically rename Denote buffers when opening them so that
;; instead of their long file name they have a literal "[D]"
;; followed by the file's title. Read the doc string of
;; `denote-rename-buffer-format' for how to modify this.
(denote-rename-buffer-mode 1)
(with-eval-after-load 'denote
(setq denote-directory (expand-file-name "~/Documents/notes/"))
(setq denote-file-type 'text) ; Org is the default file type
(setq denote-journal-extras-directory nil) ; use the `denote-directory'
(setq denote-journal-extras-title-format nil) ; always prompt for title
(setq denote-journal-extras-keyword "journal")
(setq denote-known-keywords '("emacs" "philosophy" "politics"))
(setq denote-infer-keywords t)
(setq denote-sort-keywords t)
(setq denote-excluded-directories-regexp nil)
(setq denote-date-format nil)
(setq denote-rename-confirmations nil) ; CAREFUL with this if you are not familiar with Denote!
(setq denote-backlinks-show-context nil)
(setq denote-buffer-name-prefix "[D] ")
(setq denote-rename-buffer-format "%D")
(with-eval-after-load 'org-capture
(setq denote-org-capture-specifiers "%l\n%i\n%?")
(add-to-list 'org-capture-templates
'("n" "New note (with denote.el)" plain
(file denote-last-path)
#'denote-org-capture
:no-save t
:immediate-finish nil
:kill-buffer t
:jump-to-captured t))
(denote-rename-buffer-mode 1)
;; This prompts for TITLE, KEYWORDS, and SUBDIRECTORY
(add-to-list 'org-capture-templates
'("N" "New note with prompts (with denote.el)" plain
(file denote-last-path)
(function
(lambda ()
(denote-org-capture-with-prompts :title :keywords :signature)))
:no-save t
:immediate-finish nil
:kill-buffer t
:jump-to-captured t))))
;; ;; EXPERIMENT 2025-04-25: This is not the default order, though
;; ;; Denote supports any order for its file name components.
;; (setq denote-file-name-components-order '(identifier signature keywords title))
(defun prot/denote-rename-all-to-reorder-components ()
"Call `denote-dired-rename-files' without any prompts.
In other words, preserve the value of each Denote file name component.
Use this command if you want to modify the user option
`denote-file-name-components-order' and then want your files to
retroactively follow that order."
(interactive)
(let ((denote-prompts nil))
(call-interactively 'denote-dired-rename-files)))))
;;;; Integrate Consult with Denote
(when prot-emacs-completion-extras
(prot-emacs-configure
(prot-emacs-install consult-denote)
(prot-emacs-keybind global-map
"C-c n f" #'consult-denote-find
"C-c n g" #'consult-denote-grep)
(consult-denote-mode 1)))
;;;; Denote Org extras (denote-org)
(prot-emacs-configure
(prot-emacs-install denote-org))
;;;; Denote Sequence notes or folgezettel (denote-sequence)
(prot-emacs-configure
(prot-emacs-install denote-sequence)
(prot-emacs-keybind global-map
"C-c n s s" #'denote-sequence
"C-c n s f" #'denote-sequence-find
"C-c n s l" #'denote-sequence-link
"C-c n s d" #'denote-sequence-dired
"C-c n s r" #'denote-sequence-reparent
"C-c n s c" #'denote-sequence-convert)
(setq denote-sequence-scheme 'alphanumeric))
;;;; Denote Markdown extras (denote-markdown)
(prot-emacs-configure
(prot-emacs-install denote-markdown))
;;;; Denote Journal extras (denote-journal)
(prot-emacs-configure
(prot-emacs-install denote-journal)
(add-hook 'calendar-mode-hook #'denote-journal-calendar-mode)
(defun prot/denote-journal-new-or-existing-entry ()
"EXPERIMENTAL Like `denote-journal-new-or-existing-entry' but with no front matter."
(interactive)
(cl-letf (((symbol-function #'denote--format-front-matter) (lambda (&rest _) ""))
(denote-file-type 'text)
(denote-journal-title-format ""))
(let* ((internal-date (current-time))
(files (denote-journal--entry-today internal-date)))
(if files
(find-file (denote-journal-select-file-prompt files))
(call-interactively 'denote-journal-new-entry)))))
(define-key global-map (kbd "C-c n j") #'prot/denote-journal-new-or-existing-entry)
(with-eval-after-load 'denote
(setq denote-journal-directory (expand-file-name "journal" denote-directory))
(setq denote-journal-keyword "journal")
(setq denote-journal-title-format 'day-date-month-year)))
;;;; Denote Silo extras (denote-silo)
(prot-emacs-configure
(prot-emacs-install denote-silo)
(with-eval-after-load 'denote
(setq denote-silo-directories
(append (denote-directories)
(list "~/Documents/books/" "~/Documents/denote-test-silo/")))))
;;; Custom extensions for "focus mode" (logos.el)
;; Read the manual: <https://protesilaos.com/emacs/logos>.
(use-package olivetti
:ensure t
:commands (olivetti-mode)
:config
(setq olivetti-body-width 0.7)
(prot-emacs-configure
(prot-emacs-install olivetti)
(setq-default olivetti-body-width 0.7)
(setq olivetti-minimum-body-width 80)
(setq olivetti-recall-visual-line-mode-entry-state t))
(setq olivetti-recall-visual-line-mode-entry-state t)
(use-package logos
:ensure t
:bind
(("C-x n n" . logos-narrow-dwim)
("C-x ]" . logos-forward-page-dwim)
("C-x [" . logos-backward-page-dwim)
;; I don't think I ever saw a package bind M-] or M-[...
("M-]" . logos-forward-page-dwim)
("M-[" . logos-backward-page-dwim)
("<f9>" . logos-focus-mode))
:config
(setq logos-outlines-are-pages t)
(setq logos-outline-regexp-alist
`((emacs-lisp-mode . ,(format "\\(^;;;+ \\|%s\\)" logos-page-delimiter))
(org-mode . ,(format "\\(^\\*+ +\\|^-\\{5\\}$\\|%s\\)" logos-page-delimiter))
(markdown-mode . ,(format "\\(^\\#+ +\\|^[*-]\\{5\\}$\\|^\\* \\* \\*$\\|%s\\)" logos-page-delimiter))
(conf-toml-mode . "^\\[")))
(prot-emacs-install logos)
;; These apply when `logos-focus-mode' is enabled. Their value is
;; buffer-local.
(setq-default logos-hide-mode-line t)
(setq-default logos-hide-header-line t)
(setq-default logos-hide-buffer-boundaries t)
(setq-default logos-hide-fringe t)
(setq-default logos-variable-pitch t) ; see my `fontaine' configurations
(setq-default logos-buffer-read-only nil)
(setq-default logos-scroll-lock nil)
(setq-default logos-olivetti t)
(prot-emacs-keybind global-map
"C-x n n" #'logos-narrow-dwim
"C-x ]" #'logos-forward-page-dwim
"C-x [" #'logos-backward-page-dwim
"M-]" #'logos-forward-page-dwim
"M-[" #'logos-backward-page-dwim
"M-n" #'logos-forward-page-dwim
"M-p" #'logos-backward-page-dwim
"<f9>" #'logos-focus-mode)
(add-hook 'enable-theme-functions #'logos-update-fringe-in-buffers)
(with-eval-after-load 'logos
(setq logos-outlines-are-pages t)
(setq logos-outline-regexp-alist
`((emacs-lisp-mode . ,(format "\\(^;;;+ \\|%s\\)" logos-page-delimiter))
(org-mode . ,(format "\\(^\\*+ +\\|^-\\{5\\}$\\|%s\\)" logos-page-delimiter))
(markdown-mode . ,(format "\\(^\\#+ +\\|^[*-]\\{5\\}$\\|^\\* \\* \\*$\\|%s\\)" logos-page-delimiter))
(conf-toml-mode . "^\\[")))
;; These apply when `logos-focus-mode' is enabled. Their value is
;; buffer-local.
(setq-default logos-hide-mode-line t)
(setq-default logos-hide-header-line t)
(setq-default logos-hide-buffer-boundaries t)
(setq-default logos-hide-fringe t)
(setq-default logos-variable-pitch t) ; see my `fontaine' configurations
(setq-default logos-buffer-read-only nil)
(setq-default logos-scroll-lock nil)
(setq-default logos-olivetti t)
(add-hook 'enable-theme-functions #'logos-update-fringe-in-buffers)
;;;; Extra tweaks
;; place point at the top when changing pages, but not in `prog-mode'
(defun prot/logos--recenter-top ()
"Use `recenter' to reposition the view at the top."
(unless (derived-mode-p 'prog-mode)
(recenter 1))) ; Use 0 for the absolute top
;; place point at the top when changing pages, but not in `prog-mode'
(defun prot/logos-recenter-top ()
"Use `recenter' to reposition the view at the top."
(unless (derived-mode-p 'prog-mode)
(recenter 1))) ; Use 0 for the absolute top
(add-hook 'logos-page-motion-hook #'prot/logos--recenter-top))
(add-hook 'logos-page-motion-hook #'prot/logos-recenter-top)))
(provide 'prot-emacs-langs)

View file

@ -1,21 +1,61 @@
;;; Minibuffer and Completions in Tandem or Minibuffer Confines Transcended (mct)
;; Read the manual: <https://protesilaos.com/emacs/mct>.
(use-package mct
:ensure t
:hook (after-init . mct-mode)
:config
(prot-emacs-configure
(prot-emacs-install mct)
(setq mct-hide-completion-mode-line t)
(setq mct-completing-read-multiple-indicator t)
;; The blocklist and passlist accept either commands/functions or
;; completion categories.
(setq mct-completion-blocklist '(notmuch-mua-new-mail notmuch-mua-prompt-for-sender))
(setq mct-completion-passlist
'( consult-buffer consult-location embark-keybinding
imenu prot-search-outline select-frame-by-name))
'(;; Some commands
prot-search-outline
select-frame-by-name
Info-goto-node
Info-index
Info-menu
vc-retrieve-tag
;; Some completion categories
consult-buffer
consult-location
embark-keybinding
imenu
file
project-file
buffer
kill-ring))
(setq mct-remove-shadowed-file-names t)
(setq mct-completion-window-size (cons #'mct-frame-height-third 1))
(setq mct-persist-dynamic-completion nil)
(setq mct-live-completion 'visible)
(setq completions-sort #'mct-sort-multi-category))
;; This is the default value but I am keeping it here in case I
;; choose to modify it.
(setq mct-sort-by-command-or-category
'((file . mct-sort-by-directory-then-by-file)
((magit-checkout vc-retrieve-tag) . mct-sort-by-alpha-then-by-length)
((kill-ring imenu consult-location Info-goto-node Info-index Info-menu) . nil) ; no sorting
(t . mct-sort-by-history)))
;; Adaptation of `icomplete-fido-backward-updir'.
(defun prot/mct-backward-updir ()
"Delete char before point or go up a directory."
(interactive nil mct-mode)
(cond
((and (eq (char-before) ?/)
(eq (mct--completion-category) 'file))
(when (string-equal (minibuffer-contents) "~/")
(delete-minibuffer-contents)
(insert (expand-file-name "~/"))
(goto-char (line-end-position)))
(save-excursion
(goto-char (1- (point)))
(when (search-backward "/" (minibuffer-prompt-end) t)
(delete-region (1+ (point)) (point-max)))))
(t (call-interactively 'backward-delete-char))))
(define-key minibuffer-local-filename-completion-map (kbd "DEL") #'prot/mct-backward-updir)
(mct-mode 1))
(provide 'prot-emacs-mct)

View file

@ -1,9 +1,10 @@
;;; Mode line
(use-package prot-modeline
:ensure nil
:config
(prot-emacs-configure
(require 'prot-modeline)
(setq mode-line-compact nil) ; Emacs 28
(setq mode-line-right-align-edge 'right-margin) ; Emacs 30
(setq prot-modeline-show-frame-name (alist-get 'undecorated initial-frame-alist))
(setq-default mode-line-format
'("%e"
prot-modeline-kbd-macro
@ -17,6 +18,8 @@
prot-modeline-major-mode
prot-modeline-process
" "
prot-modeline-frame-name
" "
prot-modeline-vc-branch
" "
prot-modeline-eglot
@ -24,15 +27,55 @@
prot-modeline-flymake
" "
mode-line-format-right-align ; Emacs 30
prot-modeline-which-function-indicator
prot-modeline-notmuch-indicator
" "
prot-modeline-misc-info))
(when prot-emacs-load-theme-family
(cond
((memq prot-emacs-load-theme-family '(modus ef standard))
(defun prot/modeline-set-faces ()
(modus-themes-with-colors
(custom-set-faces
`(prot-modeline-indicator-red ((,c :inherit bold :foreground ,red)))
`(prot-modeline-indicator-green ((,c :inherit bold :foreground ,green)))
`(prot-modeline-indicator-yellow ((,c :inherit bold :foreground ,yellow)))
`(prot-modeline-indicator-blue ((,c :inherit bold :foreground ,blue)))
`(prot-modeline-indicator-magenta ((,c :inherit bold :foreground ,magenta)))
`(prot-modeline-indicator-cyan ((,c :inherit bold :foreground ,cyan)))
`(prot-modeline-indicator-red-bg ((,c :inherit (bold prot-modeline-indicator-button) :background ,bg-red-intense :foreground ,fg-main)))
`(prot-modeline-indicator-green-bg ((,c :inherit (bold prot-modeline-indicator-button) :background ,bg-green-intense :foreground ,fg-main)))
`(prot-modeline-indicator-yellow-bg ((,c :inherit (bold prot-modeline-indicator-button) :background ,bg-yellow-intense :foreground ,fg-main)))
`(prot-modeline-indicator-blue-bg ((,c :inherit (bold prot-modeline-indicator-button) :background ,bg-blue-intense :foreground ,fg-main)))
`(prot-modeline-indicator-magenta-bg ((,c :inherit (bold prot-modeline-indicator-button) :background ,bg-magenta-intense :foreground ,fg-main)))
`(prot-modeline-indicator-cyan-bg ((,c :inherit (bold prot-modeline-indicator-button) :background ,bg-cyan-intense :foreground ,fg-main))))))
(add-hook 'modus-themes-after-load-theme-hook #'prot/modeline-set-faces))
((eq prot-emacs-load-theme-family 'doric)
(defun prot/modeline-set-faces ()
(doric-themes-with-colors
(custom-set-faces
`(prot-modeline-indicator-red ((t :inherit bold :foreground ,fg-red)))
`(prot-modeline-indicator-green ((t :inherit bold :foreground ,fg-green)))
`(prot-modeline-indicator-yellow ((t :inherit bold :foreground ,fg-yellow)))
`(prot-modeline-indicator-blue ((t :inherit bold :foreground ,fg-blue)))
`(prot-modeline-indicator-magenta ((t :inherit bold :foreground ,fg-magenta)))
`(prot-modeline-indicator-cyan ((t :inherit bold :foreground ,fg-cyan)))
`(prot-modeline-indicator-red-bg ((t :inherit (bold prot-modeline-indicator-button) :background ,bg-red :foreground ,fg-main)))
`(prot-modeline-indicator-green-bg ((t :inherit (bold prot-modeline-indicator-button) :background ,bg-green :foreground ,fg-main)))
`(prot-modeline-indicator-yellow-bg ((t :inherit (bold prot-modeline-indicator-button) :background ,bg-yellow :foreground ,fg-main)))
`(prot-modeline-indicator-blue-bg ((t :inherit (bold prot-modeline-indicator-button) :background ,bg-blue :foreground ,fg-main)))
`(prot-modeline-indicator-magenta-bg ((t :inherit (bold prot-modeline-indicator-button) :background ,bg-magenta :foreground ,fg-main)))
`(prot-modeline-indicator-cyan-bg ((t :inherit (bold prot-modeline-indicator-button) :background ,bg-cyan :foreground ,fg-main))))))
(add-hook 'doric-themes-after-load-theme-hook #'prot/modeline-set-faces)))
(prot/modeline-set-faces))
(with-eval-after-load 'spacious-padding
(defun prot/modeline-spacious-indicators ()
"Set box attribute to `'prot-modeline-indicator-button' if spacious-padding is enabled."
(if (bound-and-true-p spacious-padding-mode)
(set-face-attribute 'prot-modeline-indicator-button nil :box t)
(set-face-attribute 'prot-modeline-indicator-button nil :box '(:style flat-button))
(set-face-attribute 'prot-modeline-indicator-button nil :box 'unspecified)))
;; Run it at startup and then afterwards whenever
@ -41,22 +84,27 @@
(add-hook 'spacious-padding-mode-hook #'prot/modeline-spacious-indicators)))
;;; Keycast mode
(use-package keycast
:ensure t
:after prot-modeline
:commands (keycast-mode-line-mode keycast-header-line-mode keycast-tab-bar-mode keycast-log-mode)
:init
(setq keycast-mode-line-format "%2s%k%c%R")
(setq keycast-mode-line-insert-after 'prot-modeline-vc-branch)
(setq keycast-mode-line-window-predicate 'mode-line-window-selected-p)
(setq keycast-mode-line-remove-tail-elements nil)
:config
(dolist (input '(self-insert-command org-self-insert-command))
(add-to-list 'keycast-substitute-alist `(,input "." "Typing…")))
;;; Show the name of the current definition or heading for context (`which-function-mode')
(prot-emacs-configure
(setq which-func-modes '(prog-mode org-mode))
;; NOTE 2025-10-26: I handle the indicator on my own via `prot-modeline-which-function-indicator'.
(setq which-func-display 'mode) ; Emacs 30
(setq which-func-unknown "")
(which-function-mode 1))
(dolist (event '( mouse-event-p mouse-movement-p mwheel-scroll handle-select-window
mouse-set-point mouse-drag-region))
(add-to-list 'keycast-substitute-alist `(,event nil))))
;;; Keycast mode
(prot-emacs-configure
(prot-emacs-install keycast)
(with-eval-after-load 'prot-modeline
(setq keycast-mode-line-format "%2s%k%c%R")
(setq keycast-mode-line-insert-after 'prot-modeline-vc-branch)
(setq keycast-mode-line-window-predicate 'mode-line-window-selected-p)
(setq keycast-mode-line-remove-tail-elements nil))
(with-eval-after-load 'keycast
(dolist (input '(self-insert-command org-self-insert-command))
(add-to-list 'keycast-substitute-alist `(,input "." "Typing…")))
(dolist (event '("<mouse-event>" "<mouse-movement>" "<mouse-2>" "<drag-mouse-1>" "<wheel-up>" "<wheel-down>" "<double-wheel-up>" "<double-wheel-down>" "<triple-wheel-up>" "<triple-wheel-down>" "<wheel-left>" "<wheel-right>" handle-select-window mouse-set-point mouse-drag-region))
(add-to-list 'keycast-substitute-alist `(,event nil nil)))))
(provide 'prot-emacs-modeline)

View file

@ -2,86 +2,69 @@
;; The themes are highly customisable. Read the manual:
;; <https://protesilaos.com/emacs/modus-themes>.
(use-package modus-themes
:ensure t
:demand t
:bind (("<f5>" . modus-themes-toggle)
("C-<f5>" . modus-themes-select))
:config
(setq modus-themes-custom-auto-reload nil
modus-themes-to-toggle '(modus-operandi modus-vivendi)
;; modus-themes-to-toggle '(modus-operandi-tinted modus-vivendi-tinted)
;; modus-themes-to-toggle '(modus-operandi-deuteranopia modus-vivendi-deuteranopia)
;; modus-themes-to-toggle '(modus-operandi-tritanopia modus-vivendi-tritanopia)
modus-themes-mixed-fonts t
(prot-emacs-configure
(prot-emacs-install modus-themes)
;; Starting with version 5.0.0 of the `modus-themes', other packages
;; can be built on top to provide their own "Modus" derivatives.
;; For example, this is what I do with my `ef-themes' and
;; `standard-themes' (starting with versions 2.0.0 and 3.0.0,
;; respectively).
;;
;; The `modus-themes-include-derivatives-mode' makes all Modus
;; commands that act on a theme consider all such derivatives, if
;; their respective packages are available and have been loaded.
;;
;; Note that those packages can even completely take over from the
;; Modus themes such that, for example, `modus-themes-rotate' only
;; goes through the Ef themes (to this end, the Ef themes provide
;; the `ef-themes-take-over-modus-themes-mode' and the Standard
;; themes have the `standard-themes-take-over-modus-themes-mode'
;; equivalent).
;;
;; If you only care about the Modus themes, then (i) you do not need
;; to enable the `modus-themes-include-derivatives-mode' and (ii) do
;; not install and activate those other theme packages.
(modus-themes-include-derivatives-mode 1)
(prot-emacs-keybind global-map
"<f5>" #'modus-themes-rotate
"C-<f5>" #'modus-themes-select
"M-<f5>" #'modus-themes-load-random)
(setq modus-themes-mixed-fonts t
modus-themes-variable-pitch-ui t
modus-themes-italic-constructs t
modus-themes-bold-constructs nil
modus-themes-completions '((t . (extrabold)))
modus-themes-prompts '(extrabold)
modus-themes-bold-constructs t
modus-themes-completions '((t . (bold)))
modus-themes-prompts '(bold)
modus-themes-headings
'((agenda-structure . (variable-pitch light 2.2))
(agenda-date . (variable-pitch regular 1.3))
(t . (regular 1.15))))
(setq modus-themes-common-palette-overrides nil)
;; '((bg-mode-line-active bg-cyan-subtle)
;; (keybind yellow-warmer)))
(if (prot-emacs-theme-environment-dark-p)
(modus-themes-load-theme (cadr modus-themes-to-toggle))
(modus-themes-load-theme (car modus-themes-to-toggle))))
(if (prot-emacs-gnome-prefers-dark-p)
(modus-themes-load-random 'dark)
(modus-themes-load-random 'light)))
;; NOTE: For testing purposes
(prot-emacs-comment
(:eval nil)
(progn
(mapc #'disable-theme custom-enabled-themes)
(add-to-list 'load-path "/home/prot/Git/Projects/modus-themes/")
(add-to-list 'load-path "~/Git/Projects/modus-themes/")
(require 'modus-themes)
(require 'modus-themes)
(setq modus-themes-custom-auto-reload nil
modus-themes-to-toggle '(modus-operandi modus-vivendi)
;; modus-themes-to-toggle '(modus-operandi-tinted modus-vivendi-tinted)
;; modus-themes-to-toggle '(modus-operandi-deuteranopia modus-vivendi-deuteranopia)
;; modus-themes-to-toggle '(modus-operandi-tritanopia modus-vivendi-tritanopia)
modus-themes-mixed-fonts t
modus-themes-variable-pitch-ui nil
modus-themes-italic-constructs t
modus-themes-bold-constructs t
modus-themes-completions '((t . (extrabold)))
modus-themes-prompts nil
modus-themes-headings
'((agenda-structure . (variable-pitch light 2.2))
(agenda-date . (variable-pitch regular 1.3))
(t . (regular 1.15))))
(modus-themes-include-derivatives-mode 1)
;; (setq modus-themes-common-palette-overrides nil)
(prot-emacs-keybind global-map
"<f5>" #'modus-themes-rotate
"C-<f5>" #'modus-themes-select
"M-<f5>" #'modus-themes-load-random)
(setq modus-themes-common-palette-overrides
`((fringe unspecified)
;; (bg-mode-line-active bg-lavender)
;; (border-mode-line-active unspecified)
;; (border-mode-line-inactive unspecified)
(bg-line-number-active bg-hl-line)
(bg-line-number-inactive unspecified)
(fg-line-number-active fg-main)
;; ,@modus-themes-preset-overrides-warmer
))
;; ;; Make the active mode line have a pseudo 3D effect (this assumes
;; ;; you are using the default mode line and not an extra package).
;; (custom-set-faces
;; '(mode-line ((t :box (:style unspecified)))))
(if (prot-emacs-theme-environment-dark-p)
(modus-themes-load-theme (cadr modus-themes-to-toggle))
(modus-themes-load-theme (car modus-themes-to-toggle)))
;; Also check `modus-themes-select'. To list the palette's colours,
;; use `modus-themes-list-colors', `modus-themes-list-colors-current'.
(define-key global-map (kbd "<f5>") #'modus-themes-toggle)))
(if (prot-emacs-gnome-prefers-dark-p)
(modus-themes-load-random 'dark)
(modus-themes-load-random 'light)))
(provide 'prot-emacs-modus-themes)

View file

@ -1,12 +1,12 @@
;; This is deprecated code as I stopped using mu4e a long time ago. I
;; keep it here for when I need to test it for my themes (`ef-themes',
;; `modus-themes', `standard-themes').
;; `modus-themes', `standard-themes', `doric-themes').
(use-package mu4e
:ensure nil
;; This is an exception because I install it from the system
;; distribution's package archives (depends on non-Emacs code)
:load-path "/usr/share/emacs/site-lisp/elpa/mu4e-1.8.14"
:load-path "/usr/share/emacs/site-lisp/elpa-src/mu4e-1.8.14/"
:config
(setq mu4e-maildir "~/.20240226-mail-mu4e")
(setq mu4e-attachment-dir (expand-file-name "~/Downloads/mail-attachments/"))
@ -147,7 +147,7 @@
(setq mu4e-compose-complete-addresses nil
mu4e-compose-complete-only-personal t)
(setq mu4e-compose-signature "Protesilaos Stavrou\nprotesilaos.com\n")
(setq mu4e-compose-signature "Protesilaos\nprotesilaos.com\n")
(setq mu4e-compose-signature-auto-include t)
(setq mu4e-context-policy 'pick-first

View file

@ -3,31 +3,24 @@
;; I install notmuch from the distro's repos because the CLI program is
;; not dependent on Emacs. Though the package also includes notmuch.el
;; which is what we use here (they are maintained by the same people).
(use-package notmuch
:load-path "/usr/share/emacs/site-lisp/"
:defer t
:commands (notmuch notmuch-mua-new-mail))
(prot-emacs-configure
(add-to-list 'load-path "/usr/share/emacs/site-lisp/"))
;;; Account settings
(use-package notmuch
:defer t
:config
(let ((prv (prot-common-auth-get-field "prv" :user))
(pub (prot-common-auth-get-field "pub" :user))
(inf (prot-common-auth-get-field "inf" :user)))
(setq notmuch-identities
(mapcar (lambda (str)
(format "%s <%s>" user-full-name str))
(list prv pub inf))
notmuch-fcc-dirs
`((,prv . "prv/Sent")
(,inf . "inf/Sent")
(,pub . "pub/Sent")))))
(prot-emacs-configure
(with-eval-after-load 'prot-common
(let ((prv (prot-common-auth-get-field "prv-gandi" :user))
(pub (prot-common-auth-get-field "pub-gandi" :user))
(inf (prot-common-auth-get-field "inf-gandi" :user))
(box (prot-common-auth-get-field "prot-gandi" :user)))
(setq notmuch-identities
(mapcar (lambda (str)
(format "%s <%s>" user-full-name str))
(list prv pub inf box))
notmuch-fcc-dirs `((".*" . "gandi/Sent"))))))
;;;; General UI
(use-package notmuch
:defer t
:config
(prot-emacs-configure
(setq notmuch-show-logo nil
notmuch-column-control 1.0
notmuch-hello-auto-refresh t
@ -37,9 +30,7 @@
notmuch-show-all-tags-list t))
;;;; Search
(use-package notmuch
:defer t
:config
(prot-emacs-configure
(setq notmuch-search-oldest-first nil)
(setq notmuch-search-result-format
'(("date" . "%12s ")
@ -74,12 +65,11 @@
:query "tag:inbox"
:sort-order newest-first
:key ,(kbd "i"))
( :name "💬 unread (inbox)"
( :name "💬 all unread (inbox)"
:query "tag:unread and tag:inbox"
:sort-order newest-first
:key ,(kbd "u"))
;; My packages
( :name "🗂️ unread packages"
( :name "🛠️ unread packages"
:query "tag:unread and tag:package"
:sort-order newest-first
:key ,(kbd "p"))
@ -87,24 +77,10 @@
( :name "🏆 unread coaching"
:query "tag:unread and tag:coach"
:sort-order newest-first
:key ,(kbd "c"))
;; Emacs
( :name "🔨 emacs-devel"
:query "(from:emacs-devel@gnu.org or to:emacs-devel@gnu.org) not tag:archived"
:sort-order newest-first
:key ,(kbd "e d"))
( :name "🦄 emacs-orgmode"
:query "(from:emacs-orgmode@gnu.org or to:emacs-orgmode@gnu.org) not tag:archived"
:sort-order newest-first
:key ,(kbd "e o"))
( :name "🐛 emacs-bugs"
:query "'to:\"/*@debbugs.gnu.org*/\"' not tag:archived"
:sort-order newest-first :key ,(kbd "e b")))))
:key ,(kbd "c")))))
;;;; Tags
(use-package notmuch
:defer t
:config
(prot-emacs-configure
(setq notmuch-archive-tags nil ; I do not archive email
notmuch-message-replied-tags '("+replied")
notmuch-message-forwarded-tags '("+forwarded")
@ -133,12 +109,11 @@
(concat "🏷️" tag)))))
;;;; Email composition
(use-package notmuch
:defer t
:config
(prot-emacs-configure
(setq notmuch-mua-compose-in 'current-window)
(setq notmuch-mua-hidden-headers nil)
(setq notmuch-address-command 'internal) ; NOTE 2024-01-09: I am not using this and must review it.
(setq notmuch-address-command 'internal)
(setq notmuch-address-use-company nil)
(setq notmuch-always-prompt-for-sender t)
(setq notmuch-mua-cite-function 'message-cite-original-without-signature)
(setq notmuch-mua-reply-insert-header-p-function 'notmuch-show-reply-insert-header-p-never)
@ -149,12 +124,31 @@
(setq notmuch-mua-attachment-regexp ; see `notmuch-mua-send-hook'
(concat "\\b\\(attache\?ment\\|attached\\|attach\\|"
"pi[èe]ce\s+jointe?\\|"
"συνημμ[εέ]νο\\|επισυν[αά]πτω\\)\\b")))
"συνημμ[εέ]νο\\|επισυν[αά]πτω\\)\\b"))
(with-eval-after-load 'message
(defun prot-notmuch-message-tab ()
"Override for `message-tab' to enforce header line check.
More specifically, perform address completion when on a relevant header
line, because `message-tab' sometimes (not sure when/how) fails to do
that and instead tries to complete against dictionary entries."
(interactive nil message-mode)
(cond
((save-excursion
(goto-char (line-beginning-position))
(looking-at notmuch-address-completion-headers-regexp))
(notmuch-address-expand-name)
;; Completion was performed; nothing else to do.
nil)
(message-tab-body-function (funcall message-tab-body-function))
(t (funcall (or (lookup-key text-mode-map "\t")
(lookup-key global-map "\t")
'indent-relative)))))
(advice-add #'message-tab :override #'prot-notmuch-message-tab)))
;;;; Reading messages
(use-package notmuch
:defer t
:config
(prot-emacs-configure
(setq notmuch-show-relative-dates t)
(setq notmuch-show-all-multipart/alternative-parts nil)
(setq notmuch-show-indent-messages-width 0)
@ -171,87 +165,100 @@
notmuch-wash-citation-lines-suffix count)))
;;;; Hooks and key bindings
(use-package notmuch
:hook
(notmuch-mua-send . notmuch-mua-attachment-check)
(notmuch-show . (lambda () (setq-local header-line-format nil)))
:config
(prot-emacs-configure
(add-hook 'notmuch-mua-send-hook #'notmuch-mua-attachment-check) ; also see `notmuch-mua-attachment-regexp'
(add-hook 'notmuch-show-hook (lambda () (setq-local header-line-format nil)))
(remove-hook 'notmuch-show-hook #'notmuch-show-turn-on-visual-line-mode)
(remove-hook 'notmuch-search-hook #'notmuch-hl-line-mode) ; Check my `lin' package
:bind
( :map global-map
("C-c m" . notmuch)
("C-x m" . notmuch-mua-new-mail) ; override `compose-mail'
:map notmuch-search-mode-map ; I normally don't use the tree view, otherwise check `notmuch-tree-mode-map'
("/" . notmuch-search-filter) ; alias for l
("r" . notmuch-search-reply-to-thread) ; easier to reply to all by default
("R" . notmuch-search-reply-to-thread-sender)
:map notmuch-show-mode-map
("r" . notmuch-show-reply) ; easier to reply to all by default
("R" . notmuch-show-reply-sender)
:map notmuch-hello-mode-map
("C-<tab>" . nil)))
(prot-emacs-keybind global-map
"C-c m" #'notmuch
"C-x m" #'notmuch-mua-new-mail) ; override `compose-mail'
(with-eval-after-load 'notmuch
(prot-emacs-keybind notmuch-search-mode-map ; I normally don't use the tree view, otherwise check `notmuch-tree-mode-map'
"a" nil ; the default is too easy to hit accidentally and I do not archive stuff
"A" nil
"/" #'notmuch-search-filter ; alias for l
"r" #'notmuch-search-reply-to-thread ; easier to reply to all by default
"R" #'notmuch-search-reply-to-thread-sender)
(prot-emacs-keybind notmuch-show-mode-map
"a" nil ; the default is too easy to hit accidentally and I do not archive stuff
"A" nil
"r" #'notmuch-show-reply ; easier to reply to all by default
"R" #'notmuch-show-reply-sender)
(define-key notmuch-hello-mode-map (kbd "C-<tab>") nil)))
;;; My own tweaks for notmuch (prot-notmuch.el)
(use-package prot-notmuch
:ensure nil
:after notmuch
:bind
( :map notmuch-search-mode-map
("a" . nil) ; the default is too easy to hit accidentally
("A" . nil)
("D" . prot-notmuch-search-delete-thread)
("S" . prot-notmuch-search-spam-thread)
("g" . prot-notmuch-refresh-buffer)
:map notmuch-show-mode-map
("a" . nil) ; the default is too easy to hit accidentally
("A" . nil)
("D" . prot-notmuch-show-delete-message)
("S" . prot-notmuch-show-spam-message)
:map notmuch-show-stash-map
("S" . prot-notmuch-stash-sourcehut-link))
:config
;; Those are for the actions that are available after pressing 'k'
;; (`notmuch-tag-jump'). For direct actions, refer to the key
;; bindings below.
(setq notmuch-tagging-keys
`((,(kbd "d") prot-notmuch-mark-delete-tags "💥 Mark for deletion")
(,(kbd "f") prot-notmuch-mark-flag-tags "🚩 Flag as important")
(,(kbd "s") prot-notmuch-mark-spam-tags "🔥 Mark as spam")
(,(kbd "r") ("-unread") "👁️‍🗨️ Mark as read")
(,(kbd "u") ("+unread") "🗨️ Mark as unread")))
(prot-emacs-configure
(with-eval-after-load 'notmuch
(require 'prot-notmuch)
;; These emoji are purely cosmetic. The tag remains the same: I
;; would not like to input emoji for searching.
(add-to-list 'notmuch-tag-formats '("encrypted" (concat tag "🔒")))
(add-to-list 'notmuch-tag-formats '("attachment" (concat tag "📎")))
(add-to-list 'notmuch-tag-formats '("coach" (concat tag "🏆")))
(add-to-list 'notmuch-tag-formats '("package" (concat tag "🗂️"))))
(prot-emacs-keybind notmuch-search-mode-map
"D" #'prot-notmuch-search-delete-thread
"S" #'prot-notmuch-search-spam-thread
"g" #'prot-notmuch-refresh-buffer)
(prot-emacs-keybind notmuch-show-mode-map
"D" #'prot-notmuch-show-delete-message
"S" #'prot-notmuch-show-spam-message)
(define-key notmuch-show-stash-map (kbd "S") #'prot-notmuch-stash-sourcehut-link)
;; Those are for the actions that are available after pressing 'k'
;; (`notmuch-tag-jump'). For direct actions, refer to the key
;; bindings below.
(setq notmuch-tagging-keys
`((,(kbd "d") prot-notmuch-mark-delete-tags "💥 Mark for deletion")
(,(kbd "f") prot-notmuch-mark-flag-tags "🚩 Flag as important")
(,(kbd "s") prot-notmuch-mark-spam-tags "🔥 Mark as spam")
(,(kbd "r") ("-unread") "👁️‍🗨️ Mark as read")
(,(kbd "u") ("+unread") "🗨️ Mark as unread")))
;; These emoji are purely cosmetic. The tag remains the same: I
;; would not like to input emoji for searching.
(add-to-list 'notmuch-tag-formats '("encrypted" (concat tag "🔒")))
(add-to-list 'notmuch-tag-formats '("attachment" (concat tag "📎")))
(add-to-list 'notmuch-tag-formats '("coach" (concat tag "🏆")))
(add-to-list 'notmuch-tag-formats '("package" (concat tag "🗂️")))))
;;; Glue code for notmuch and org-link (ol-notmuch.el)
(use-package ol-notmuch
:ensure t
:after notmuch)
(prot-emacs-configure
(prot-emacs-install ol-notmuch)
(with-eval-after-load 'notmuch
(require 'ol-notmuch)))
;;; notmuch-indicator (another package of mine)
(use-package notmuch-indicator
:ensure t
:after notmuch
:config
(setq notmuch-indicator-args
'((:terms "tag:unread and tag:inbox" :label "[A] " :face prot-modeline-indicator-green)
(:terms "tag:unread and tag:inbox and not tag:package and not tag:coach" :label "[U] " :face prot-modeline-indicator-cyan)
(:terms "tag:unread and tag:package" :label "[P] " :face prot-modeline-indicator-magenta)
(:terms "tag:unread and tag:coach" :label "[C] " :face prot-modeline-indicator-red))
(prot-emacs-configure
(prot-emacs-install notmuch-indicator)
(with-eval-after-load 'notmuch
(setq notmuch-indicator-args
'(( :terms "tag:unread and tag:inbox"
:label "[U] "
;; :label "💬 "
:label-face prot-modeline-indicator-cyan
:counter-face prot-modeline-indicator-cyan)
( :terms "tag:unread and tag:package"
:label "[P] "
;; :label "🛠️ "
:label-face prot-modeline-indicator-magenta
:counter-face prot-modeline-indicator-magenta)
( :terms "tag:unread and tag:coach"
:label "[C] "
;; :label "🏆 "
:label-face prot-modeline-indicator-red
:counter-face prot-modeline-indicator-red))
notmuch-indicator-refresh-count (* 60 3)
notmuch-indicator-hide-empty-counters t
notmuch-indicator-force-refresh-commands '(notmuch-refresh-this-buffer))
notmuch-indicator-refresh-count (* 60 3)
notmuch-indicator-hide-empty-counters t
notmuch-indicator-force-refresh-commands '(notmuch-refresh-this-buffer))
;; I control its placement myself. See prot-emacs-modeline.el where
;; I set the `mode-line-format'.
(setq notmuch-indicator-add-to-mode-line-misc-info nil)
;; I control its placement myself. See prot-emacs-modeline.el where
;; I set the `mode-line-format'.
(setq notmuch-indicator-add-to-mode-line-misc-info nil)
(notmuch-indicator-mode 1))
(notmuch-indicator-mode 1)))
(provide 'prot-emacs-notmuch)

View file

@ -1,38 +1,52 @@
;;; Calendar
(use-package calendar
:ensure nil
:commands (calendar)
:config
(setq calendar-mark-diary-entries-flag nil)
(setq calendar-mark-holidays-flag t)
(setq calendar-mode-line-format nil)
(setq calendar-time-display-form
'( 24-hours ":" minutes
(when time-zone (format "(%s)" time-zone))))
(setq calendar-week-start-day 1) ; Monday
(setq calendar-date-style 'iso)
(setq calendar-time-zone-style 'numeric) ; Emacs 28.1
(prot-emacs-configure
(with-eval-after-load 'calendar
(setq calendar-mark-diary-entries-flag nil)
(setq calendar-mark-holidays-flag t)
(setq calendar-mode-line-format nil)
(setq calendar-time-display-form
'( 24-hours ":" minutes
(when time-zone (format "(%s)" time-zone))))
(setq calendar-week-start-day 1) ; Monday
(setq calendar-date-style 'iso)
(setq calendar-time-zone-style 'numeric) ; Emacs 28.1
(require 'solar)
(setq calendar-latitude 35.17 ; Not my actual coordinates
calendar-longitude 33.36)
(require 'solar)
(setq calendar-latitude 35.17 ; Not my actual coordinates
calendar-longitude 33.36)
(require 'cal-dst)
(setq calendar-standard-time-zone-name "+0200")
(setq calendar-daylight-time-zone-name "+0300"))
(require 'cal-dst)
(setq calendar-standard-time-zone-name "+0200")
(setq calendar-daylight-time-zone-name "+0300")))
(prot-emacs-configure
(prot-emacs-install-vc
institution-calendar
"https://github.com/protesilaos/institution-calendar.git")
;; If you want to permanently change what M-x calendar shows, enable
;; `institution-calendar-mode'. Otherwise, use the relevant command:
;;
;; `institution-calendar-cambridge-university'
;; `institution-calendar-oxford-university'
;;
;; And so on for newly registered institutions.
;; Remember to read the manual on how to register your own institution!
(setopt institution-calendar-entity 'oxford-university)
(setopt institution-calendar-include-extra-week-numbers t)
(setopt institution-calendar-include-intermonth-header nil))
;;; Appt (appointment reminders which also integrate with Org agenda)
(use-package appt
:ensure nil
:commands (appt-activate)
:config
(setq appt-display-diary nil
appt-display-format nil
appt-display-mode-line t
appt-display-interval 3
appt-audible nil ; TODO 2023-01-25: t does nothing because I disable `ring-bell-function'?
appt-warning-time-regexp "appt \\([0-9]+\\)" ; This is for the diary
appt-message-warning-time 6)
(prot-emacs-configure
(setq appt-display-diary nil)
(setq appt-display-format nil)
(setq appt-display-mode-line t)
(setq appt-display-interval 3)
(setq appt-audible nil) ; TODO 2023-01-25: t does nothing because I disable `ring-bell-function'?
(setq appt-warning-time-regexp "appt \\([0-9]+\\)") ; This is for the diary
(setq appt-message-warning-time 6)
(with-eval-after-load 'org-agenda
(appt-activate 1)
@ -44,463 +58,464 @@
(org-agenda-to-appt)))
;;; Org-mode (personal information manager)
(use-package org
:ensure nil
:init
;; NOTE 2023-05-20: Must be evaluated before Org is loaded,
;; otherwise we have to use the Custom UI. No thanks!
(setq org-export-backends '(html texinfo md))
(prot-emacs-configure
(setq org-directory (expand-file-name "~/Documents/org/"))
(setq org-imenu-depth 7)
(add-to-list 'safe-local-variable-values '(org-hide-leading-stars . t))
(add-to-list 'safe-local-variable-values '(org-hide-macro-markers . t))
:bind
( :map global-map
("C-c l" . org-store-link)
("C-c o" . org-open-at-point-global)
:map org-mode-map
;; I don't like that Org binds one zillion keys, so if I want one
;; for something more important, I disable it from here.
("C-'" . nil)
("C-," . nil)
("M-;" . nil)
("<C-return>" . nil)
("<C-S-return>" . nil)
("C-M-S-<right>" . nil)
("C-M-S-<left>" . nil)
("C-c ;" . nil)
("C-c M-l" . org-insert-last-stored-link)
("C-c C-M-l" . org-toggle-link-display)
("M-." . org-edit-special) ; alias for C-c ' (mnenomic is global M-. that goes to source)
:map org-src-mode-map
("M-," . org-edit-src-exit) ; see M-. above
:map narrow-map
("b" . org-narrow-to-block)
("e" . org-narrow-to-element)
("s" . org-narrow-to-subtree))
:config
;;;; general settings
(setq org-ellipsis "")
(setq org-adapt-indentation nil) ; No, non, nein, όχι!
(setq org-special-ctrl-a/e nil)
(setq org-special-ctrl-k nil)
(setq org-M-RET-may-split-line '((default . nil)))
(setq org-hide-emphasis-markers nil)
(setq org-hide-macro-markers nil)
(setq org-hide-leading-stars nil)
(setq org-cycle-separator-lines 0)
(setq org-structure-template-alist
'(("s" . "src")
("e" . "src emacs-lisp")
("E" . "src emacs-lisp :results value code :lexical t")
("t" . "src emacs-lisp :tangle FILENAME")
("T" . "src emacs-lisp :tangle FILENAME :mkdirp yes")
("x" . "example")
("X" . "export")
("q" . "quote")))
(setq org-catch-invisible-edits 'show)
(setq org-return-follows-link nil)
(setq org-loop-over-headlines-in-active-region 'start-level)
(setq org-modules '(ol-info ol-eww))
(setq org-use-sub-superscripts '{})
(setq org-insert-heading-respect-content t)
(setq org-read-date-prefer-future 'time)
;; See my `pulsar' package, defined elsewhere in this setup.
(with-eval-after-load 'pulsar
(dolist (hook '(org-agenda-after-show-hook org-follow-link-hook))
(add-hook hook #'pulsar-recenter-center)
(add-hook hook #'pulsar-reveal-entry))))
(prot-emacs-keybind global-map
"C-c l" #'org-store-link
"C-c o" #'org-open-at-point-global)
(with-eval-after-load 'org
(prot-emacs-keybind org-mode-map
;; I don't like that Org binds one zillion keys, so if I want one
;; for something more important, I disable it from here.
"C-a" nil
"C-d" nil
"C-S-d" nil
"C-'" nil
"C-," nil
"M-;" nil
"<C-return>" nil
"<C-S-return>" nil
"C-M-S-<right>" nil
"C-M-S-<left>" nil
"C-c ;" nil
"C-c C-x C-c" nil ; disable `org-columns', which I never use
"C-c M-l" #'org-insert-last-stored-link
"C-c C-M-l" #'org-toggle-link-display
"M-." #'org-edit-special) ; alias for C-c ' (mnenomic is global M-. that goes to source
(define-key org-src-mode-map (kbd "M-,") #'org-edit-src-exit) ; see M-. above
(prot-emacs-keybind narrow-map
"b" #'org-narrow-to-block
"e" #'org-narrow-to-element
"s" #'org-narrow-to-subtree)
(prot-emacs-keybind ctl-x-x-map
"i" #'prot-org-id-headlines
"r" #'prot-org-id-headlines-readable
"h" #'prot-org-ox-html)
;; My custom extras, which I use for the agenda and a few other Org features.
(require 'prot-org)
;;;; general settings
(setq org-ellipsis "")
(setq org-special-ctrl-a/e nil)
(setq org-special-ctrl-k nil)
(setq org-M-RET-may-split-line '((default . nil)))
(setq org-insert-heading-respect-content t)
(setq org-hide-emphasis-markers nil)
(setq org-hide-macro-markers nil)
(setq org-hide-leading-stars nil)
(setq org-cycle-separator-lines 0)
(setq org-structure-template-alist
'(("s" . "src")
("e" . "src emacs-lisp")
("E" . "src emacs-lisp :results value code :lexical t")
("t" . "src emacs-lisp :tangle FILENAME")
("T" . "src emacs-lisp :tangle FILENAME :mkdirp yes")
("x" . "example")
("X" . "export")
("q" . "quote")))
(setq org-fold-catch-invisible-edits 'show)
(setq org-yank-folded-subtrees nil)
(setq org-return-follows-link t)
(setq org-loop-over-headlines-in-active-region 'start-level)
(setq org-modules '(ol-info ol-eww))
(setq org-use-sub-superscripts '{})
(setq org-read-date-prefer-future 'time)
(setq org-highlight-latex-and-related nil) ; other options affect elisp regexp in src blocks
(setq org-fontify-quote-and-verse-blocks t)
(setq org-fontify-whole-block-delimiter-line t)
(setq org-track-ordered-property-with-tag t)
(setq org-highest-priority ?A)
(setq org-lowest-priority ?C)
(setq org-default-priority ?A)
(setq org-priority-faces nil)
;; See my `pulsar' package, defined elsewhere in this setup.
(prot-emacs-hook
(org-agenda-after-show-hook org-follow-link-hook)
(pulsar-recenter-center pulsar-reveal-entry)
nil
pulsar)))
;;;; `org-indent-mode' and initial folding
(prot-emacs-configure
(add-hook 'org-mode-hook #'org-indent-mode)
(setq org-indent-mode-turns-on-hiding-stars nil)
(setq org-adapt-indentation nil) ; No, non, nein, όχι to literal indentation!
(setq org-indent-indentation-per-level 4)
(setq org-startup-folded 'content))
;;;; refile, todo
(use-package org
:ensure nil
:config
(with-eval-after-load 'org
(setq org-refile-targets
'((org-agenda-files . (:maxlevel . 2))
(nil . (:maxlevel . 2))))
(setq org-refile-use-outline-path t)
(setq org-refile-use-outline-path nil)
(setq org-refile-allow-creating-parent-nodes 'confirm)
(setq org-refile-use-cache t)
(setq org-reverse-note-order nil)
;; ;; NOTE 2023-04-07: Leaving this here for demo purposes.
;; (setq org-todo-keywords
;; '((sequence "TODO(t)" "MAYBE(m)" "WAIT(w@/!)" "|" "CANCEL(c@)" "DONE(d!)")
;; (sequence "COACH(k)" "|" "COACHED(K!)")))
(setq org-todo-keywords
'((sequence "TODO(t)" "|" "CANCEL(c@)" "DONE(d!)")
(sequence "COACH(k)" "|" "COACHED(K!)")))
(defface prot/org-bold-done
'((t :inherit (bold org-done)))
"Face for bold DONE-type Org keywords.")
(setq org-todo-keywords
'((sequence "TODO(t)" "MAYBE(m)" "|" "CANCELLED(c@)" "DONE(d!)")))
(defface prot/org-todo-alternative
'((t :inherit (italic org-todo)))
"Face for alternative TODO-type Org keywords.")
(defface prot/org-done-alternative
'((t :inherit (italic org-done)))
"Face for alternative DONE-type Org keywords.")
(setq org-todo-keyword-faces
'(("CANCEL" . prot/org-bold-done)))
'(("MAYBE" . prot/org-todo-alternative)
("CANCELLED" . prot/org-done-alternative)))
(defface prot/org-tag-coaching
'((default :inherit unspecified :weight regular :slant normal)
(((class color) (min-colors 88) (background light))
:foreground "#004476")
(((class color) (min-colors 88) (background dark))
:foreground "#c0d0ef")
(t :foreground "cyan"))
"Face for coaching Org tag.")
(defface prot/org-tag-protasks
'((default :inherit unspecified :weight regular :slant normal)
(((class color) (min-colors 88) (background light))
:foreground "#603f00")
(((class color) (min-colors 88) (background dark))
:foreground "#deba66")
(t :foreground "yellow"))
"Face for protasks Org tag.")
(when (eq prot-emacs-load-theme-family 'modus)
(setq org-tag-faces
'(("coaching" . prot/org-tag-coaching)
("protasks" . prot/org-tag-protasks))))
(setq org-use-fast-todo-selection 'expert)
(setq org-priority-faces nil)
(setq org-fontify-done-headline nil)
(setq org-fontify-todo-headline nil)
(setq org-fontify-quote-and-verse-blocks t)
(setq org-fontify-whole-heading-line nil)
(setq org-fontify-whole-block-delimiter-line t)
(setq org-highlight-latex-and-related nil) ; other options affect elisp regexp in src blocks
(setq org-enforce-todo-dependencies t)
(setq org-enforce-todo-checkbox-dependencies t)
(setq org-track-ordered-property-with-tag t)
(setq org-highest-priority ?A)
(setq org-lowest-priority ?C)
(setq org-default-priority ?A))
(setq org-enforce-todo-checkbox-dependencies t))
;;;; tags
(use-package org
:ensure nil
:config
(setq org-tag-alist ; I don't really use those, but whatever
'(("meeting")
("admin")
("emacs")
("modus")
("politics")
("economics")
("philosophy")
("book")
("essay")
("mail")
("purchase")
("hardware")
("software")
("website")))
(setq org-auto-align-tags nil)
(setq org-tags-column 0))
(prot-emacs-configure
(with-eval-after-load 'org
(setq org-tag-alist nil)
(setq org-auto-align-tags nil)
(setq org-tags-column 0)))
;;;; log
(use-package org
:ensure nil
:config
(setq org-log-done 'time)
(setq org-log-into-drawer t)
(setq org-log-note-clock-out nil)
(setq org-log-redeadline 'time)
(setq org-log-reschedule 'time))
(prot-emacs-configure
(with-eval-after-load 'org
(setq org-log-done 'time)
(setq org-log-into-drawer t)
(setq org-log-note-clock-out nil)
(setq org-log-redeadline 'time)
(setq org-log-reschedule 'time)))
;;;; links
(use-package org
:ensure nil
:config
(setq org-link-keep-stored-after-insertion nil))
;; TODO 2021-10-15 org-link-make-description-function
(prot-emacs-configure
(with-eval-after-load 'org
(setq org-return-follows-link t)
(setq org-link-context-for-files t)
(setq org-link-keep-stored-after-insertion nil)
(setq org-id-link-to-org-use-id 'create-if-interactive-and-no-custom-id)))
;;;; code blocks
(use-package org
:ensure nil
:config
(setq org-confirm-babel-evaluate nil)
(setq org-src-window-setup 'current-window)
(setq org-edit-src-persistent-message nil)
(setq org-src-fontify-natively t)
(setq org-src-preserve-indentation t)
(setq org-src-tab-acts-natively t)
(setq org-edit-src-content-indentation 0))
(prot-emacs-configure
(with-eval-after-load 'org
(setq org-confirm-babel-evaluate nil)
(setq org-src-window-setup 'current-window)
(setq org-edit-src-persistent-message nil)
(setq org-src-fontify-natively t)
(setq org-src-preserve-indentation t)
(setq org-src-tab-acts-natively t)
(setq org-edit-src-content-indentation 0)))
;;;; export
(use-package org
:ensure nil
:config
(setq org-export-with-toc t)
(setq org-export-headline-levels 8)
(setq org-export-dispatch-use-expert-ui nil)
(setq org-html-htmlize-output-type nil)
(setq org-html-head-include-default-style nil)
(setq org-html-head-include-scripts nil))
(prot-emacs-configure
;; NOTE 2023-05-20: Must be evaluated before Org is loaded,
;; otherwise we have to use the Custom UI. No thanks!
(setq org-export-backends '(html texinfo md))
;;;; IDs
(use-package org
:ensure nil
:config
(setq org-id-link-to-org-use-id 'create-if-interactive-and-no-custom-id))
(with-eval-after-load 'org
(setq org-export-with-toc t)
(setq org-export-headline-levels 8)
(setq org-export-dispatch-use-expert-ui nil)
(setq org-html-htmlize-output-type nil)
(setq org-html-head-include-default-style nil)
(setq org-html-head-include-scripts nil)))
;;;; capture
(use-package org-capture
:ensure nil
:bind ("C-c c" . org-capture)
:config
(require 'prot-org)
(prot-emacs-configure
(define-key global-map (kbd "C-c c") #'org-capture)
(setq org-capture-templates
`(("b" "Basic task for future review" entry
(file+headline "tasks.org" "Tasks to be reviewed")
,(concat "* %^{Title}\n"
":PROPERTIES:\n"
":CAPTURED: %U\n"
":END:\n\n"
"%i %l")
:empty-lines-after 1)
("c" "Clock in to a task" entry
(file+headline "tasks.org" "Clocked tasks")
,(concat "* TODO %^{Title}\n"
":PROPERTIES:\n"
":EFFORT: %^{Effort estimate in minutes|5|10|15|30|45|60|90|120}\n"
":END:\n\n"
"%a\n")
:prepend t
:clock-in t
:clock-keep t
:immediate-finish t
:empty-lines-after 1)
("m" "Memorandum of conversation" entry
(file+headline "tasks.org" "Tasks to be reviewed")
,(concat "* Memorandum of conversation with %^{Person}\n"
":PROPERTIES:\n"
":CAPTURED: %U\n"
":END:\n\n"
"%i%?")
:empty-lines-after 1)
("t" "Task with a due date" entry
(file+headline "tasks.org" "Tasks with a date")
,(concat "* TODO %^{Title} %^g\n"
"SCHEDULED: %^t\n"
":PROPERTIES:\n"
":CAPTURED: %U\n"
":END:\n\n"
"%a\n%i%?")
:empty-lines-after 1)
("e" "Email note" entry
(file+headline "tasks.org" "Tasks to be reviewed")
,(concat "* TODO %:subject :mail:\n"
":PROPERTIES:\n"
":CAPTURED: %U\n"
":END:\n\n"
"%a\n%i%?")
:empty-lines-after 1)
("p" "Private lesson or service" entry
(file "coach.org")
#'prot-org-capture-coach
:prepend t
:empty-lines 1)
("P" "Private service clocked" entry
(file+headline "coach.org" "Clocked services")
#'prot-org-capture-coach-clock
:prepend t
:clock-in t
:clock-keep t
:immediate-finish t
:empty-lines 1)))
(with-eval-after-load 'org-capture
(setq org-capture-templates-contexts
'(("e" ((in-mode . "notmuch-search-mode")
(in-mode . "notmuch-show-mode")
(in-mode . "notmuch-tree-mode"))))))
(require 'prot-org)
(setq org-capture-templates
(let* ((without-time (concat ":PROPERTIES:\n"
":CAPTURED: %U\n"
":CUSTOM_ID: h:%(format-time-string \"%Y%m%dT%H%M%S\")\n"
":END:\n\n"
"%a\n%?"))
(with-time (concat "DEADLINE: %^T\n"
":PROPERTIES:\n"
":CAPTURED: %U\n"
":CUSTOM_ID: h:%(format-time-string \"%Y%m%dT%H%M%S\")\n"
":APPT_WARNTIME: 20\n"
":END:\n\n"
"%a%?")))
`(("s" "Select file and heading to add to" entry
(function prot-org-select-heading-in-file)
,(concat "* TODO %^{Title}%?\n" without-time)
:empty-lines-after 1)
("t" "Task to do" entry
(file+headline "tasks.org" "All tasks")
,(concat "* TODO %^{Title} %^g\n" without-time)
:empty-lines-after 1)
("w" "Wishlist" entry
(file+olp "tasks.org" "All tasks" "Wishlist")
,(concat "* %^{Title} %^g\n" without-time)
:empty-lines-after 1)
("·" "") ; HACK to get a divider
("a" "Appointment" entry
(file+headline "tasks.org" "Appointments")
,(concat "* TODO %^{Title}\n" with-time)
:empty-lines-after 1)
("c" "Coaching appointment" entry
(file "coach.org")
,(concat "* TODO %^{Person and description}\n" with-time)
:prepend t
:empty-lines 1)
("p" "Prot Asks" entry
(file+headline "tasks.org" "Prot Asks")
,(concat "* TODO %^{Title}\n" with-time)
:empty-lines-after 1)
("v" "Video call" entry
(file+headline "tasks.org" "Video calls")
,(concat "* TODO %^{Title}\n" with-time)
:empty-lines-after 1))))))
;;;; agenda
(use-package org-agenda
:ensure nil
:bind
(prot-emacs-configure
;; I bind `org-agenda' to C-c A, so this one puts me straight into my
;; custom block agenda.
( :map global-map
("C-c A" . org-agenda)
("C-c a" . (lambda ()
"Call Org agenda with `prot-org-custom-daily-agenda' configuration."
(interactive)
(org-agenda nil "A")))
:map ctl-x-x-map
("i" . prot-org-id-headlines)
("h" . prot-org-ox-html))
:config
(prot-emacs-keybind global-map
"C-c A" #'org-agenda
"C-c a" (lambda ()
"Call Org agenda with `prot-org-custom-daily-agenda' configuration."
(interactive)
(org-agenda nil "A")))
(with-eval-after-load 'org-agenda
(prot-emacs-keybind org-agenda-mode-map
"n" #'org-agenda-next-item
"p" #'org-agenda-previous-item)
;;;;; Custom agenda blocks
(require 'prot-org)
(require 'prot-org)
(setq org-agenda-format-date #'prot-org-agenda-format-date-aligned)
(setq org-agenda-format-date #'prot-org-agenda-format-date-aligned)
;; Check the variable `prot-org-custom-daily-agenda' in prot-org.el
(setq org-agenda-custom-commands
`(("A" "Daily agenda and top priority tasks"
,prot-org-custom-daily-agenda
((org-agenda-fontify-priorities nil)
(org-agenda-prefix-format " %t %s")
(org-agenda-dim-blocked-tasks nil)))
("P" "Plain text daily agenda and top priorities"
,prot-org-custom-daily-agenda
((org-agenda-with-colors nil)
(org-agenda-prefix-format "%t %s")
(org-agenda-current-time-string ,(car (last org-agenda-time-grid)))
(org-agenda-fontify-priorities nil)
(org-agenda-remove-tags t))
("agenda.txt"))))
;; Check the variable `prot-org-custom-daily-agenda' in prot-org.el
(setq org-agenda-custom-commands
`(("A" "Daily agenda and top priority tasks"
,prot-org-custom-daily-agenda
((org-agenda-fontify-priorities nil)
(org-agenda-dim-blocked-tasks nil)))
("P" "Plain text daily agenda and top priorities"
,prot-org-custom-daily-agenda
((org-agenda-with-colors nil)
(org-agenda-prefix-format "%t %s")
(org-agenda-current-time-string ,(car (last org-agenda-time-grid)))
(org-agenda-fontify-priorities nil)
(org-agenda-remove-tags t))
("agenda.txt"))
("p" "Prot Asks"
,prot-org-custom-prot-asks-agenda)))
;;;;; Basic agenda setup
(setq org-default-notes-file (make-temp-file "emacs-org-notes-")) ; send it to oblivion
(setq org-agenda-files `(,org-directory))
(setq org-agenda-span 'week)
(setq org-agenda-start-on-weekday 1) ; Monday
(setq org-agenda-confirm-kill t)
(setq org-agenda-show-all-dates t)
(setq org-agenda-show-outline-path nil)
(setq org-agenda-window-setup 'current-window)
(setq org-agenda-skip-comment-trees t)
(setq org-agenda-menu-show-matcher t)
(setq org-agenda-menu-two-columns nil)
(setq org-agenda-sticky nil)
(setq org-agenda-custom-commands-contexts nil)
(setq org-agenda-max-entries nil)
(setq org-agenda-max-todos nil)
(setq org-agenda-max-tags nil)
(setq org-agenda-max-effort nil)
(setq org-default-notes-file (make-temp-file "emacs-org-notes-")) ; send it to oblivion
(setq org-agenda-files (list org-directory))
(setq org-agenda-span 'week)
(setq org-agenda-start-on-weekday 1) ; Monday
(setq org-agenda-confirm-kill t)
(setq org-agenda-show-all-dates t)
(setq org-agenda-show-outline-path nil)
(setq org-agenda-window-setup 'current-window)
(setq org-agenda-skip-comment-trees t)
(setq org-agenda-menu-show-matcher t)
(setq org-agenda-menu-two-columns nil)
(setq org-agenda-sticky nil)
(setq org-agenda-custom-commands-contexts nil)
(setq org-agenda-max-entries nil)
(setq org-agenda-max-todos nil)
(setq org-agenda-max-tags nil)
(setq org-agenda-max-effort nil)
;;;;; General agenda view options
;; NOTE 2021-12-07: Check further below my `org-agenda-custom-commands'
(setq org-agenda-prefix-format
'((agenda . " %i %-12:c%?-12t% s")
(todo . " %i %-12:c")
(tags . " %i %-12:c")
(search . " %i %-12:c")))
(setq org-agenda-sorting-strategy
'(((agenda habit-down time-up priority-down category-keep)
(todo priority-down category-keep)
(tags priority-down category-keep)
(search category-keep))))
(setq org-agenda-breadcrumbs-separator "->")
(setq org-agenda-todo-keyword-format "%-1s")
(setq org-agenda-fontify-priorities 'cookies)
(setq org-agenda-category-icon-alist nil)
(setq org-agenda-remove-times-when-in-prefix nil)
(setq org-agenda-remove-timeranges-from-blocks nil)
(setq org-agenda-compact-blocks nil)
(setq org-agenda-block-separator ?—)
;; ;; NOTE 2021-12-07: Also see my `org-agenda-custom-commands'
;; (setq org-agenda-prefix-format
;; '((agenda . " %i %-12:c%?-12t% s")
;; (todo . " %i %-12:c")
;; (tags . " %i %-12:c")
;; (search . " %i %-12:c")))
(setq org-agenda-prefix-format "%c %t %s")
(setq org-agenda-sorting-strategy
'(((agenda habit-down time-up priority-down category-keep)
(todo priority-down category-keep)
(tags priority-down category-keep)
(search category-keep))))
(setq org-agenda-breadcrumbs-separator "->")
(setq org-agenda-todo-keyword-format "%-1s")
(setq org-agenda-fontify-priorities 'cookies)
(setq org-agenda-category-icon-alist nil)
(setq org-agenda-remove-times-when-in-prefix nil)
(setq org-agenda-remove-timeranges-from-blocks nil)
(setq org-agenda-compact-blocks nil)
(setq org-agenda-block-separator ?—)
;;;;; Agenda marks
(setq org-agenda-bulk-mark-char "#")
(setq org-agenda-persistent-marks nil)
(setq org-agenda-bulk-mark-char "#")
(setq org-agenda-persistent-marks nil)
;;;;; Agenda diary entries
(setq org-agenda-insert-diary-strategy 'date-tree)
(setq org-agenda-insert-diary-extract-time nil)
(setq org-agenda-include-diary nil)
;; I do not want the diary, but there is no way to disable it
;; altogether. This creates a diary file in the /tmp directory.
(setq diary-file (make-temp-file "emacs-diary-"))
(setq org-agenda-diary-file 'diary-file) ; TODO 2023-05-20: review Org diary substitute
(setq org-agenda-insert-diary-strategy 'date-tree)
(setq org-agenda-insert-diary-extract-time nil)
(setq org-agenda-include-diary nil)
;; I do not want the diary, but there is no way to disable it
;; altogether. This creates a diary file in the /tmp directory.
(setq diary-file (make-temp-file "emacs-diary-"))
(setq org-agenda-diary-file 'diary-file) ; TODO 2023-05-20: review Org diary substitute
;;;;; Agenda follow mode
(setq org-agenda-start-with-follow-mode nil)
(setq org-agenda-follow-indirect t)
(setq org-agenda-start-with-follow-mode nil)
(setq org-agenda-follow-indirect t)
;;;;; Agenda multi-item tasks
(setq org-agenda-dim-blocked-tasks t)
(setq org-agenda-todo-list-sublevels t)
(setq org-agenda-dim-blocked-tasks t)
(setq org-agenda-todo-list-sublevels t)
;;;;; Agenda filters and restricted views
(setq org-agenda-persistent-filter nil)
(setq org-agenda-restriction-lock-highlight-subtree t)
(setq org-agenda-persistent-filter nil)
(setq org-agenda-restriction-lock-highlight-subtree t)
;;;;; Agenda items with deadline and scheduled timestamps
(setq org-agenda-include-deadlines t)
(setq org-deadline-warning-days 0)
(setq org-agenda-skip-scheduled-if-done nil)
(setq org-agenda-skip-scheduled-if-deadline-is-shown t)
(setq org-agenda-skip-timestamp-if-deadline-is-shown t)
(setq org-agenda-skip-deadline-if-done nil)
(setq org-agenda-skip-deadline-prewarning-if-scheduled 1)
(setq org-agenda-skip-scheduled-delay-if-deadline nil)
(setq org-agenda-skip-additional-timestamps-same-entry nil)
(setq org-agenda-skip-timestamp-if-done nil)
(setq org-agenda-search-headline-for-time nil)
(setq org-scheduled-past-days 365)
(setq org-deadline-past-days 365)
(setq org-agenda-move-date-from-past-immediately-to-today t)
(setq org-agenda-show-future-repeats t)
(setq org-agenda-prefer-last-repeat nil)
(setq org-agenda-timerange-leaders
'("" "(%d/%d): "))
(setq org-agenda-scheduled-leaders
'("Scheduled: " "Sched.%2dx: "))
(setq org-agenda-inactive-leader "[")
(setq org-agenda-deadline-leaders
'("Deadline: " "In %3d d.: " "%2d d. ago: "))
;; Time grid
(setq org-agenda-time-leading-zero t)
(setq org-agenda-timegrid-use-ampm nil)
(setq org-agenda-use-time-grid t)
(setq org-agenda-show-current-time-in-grid t)
(setq org-agenda-current-time-string (concat "Now " (make-string 70 ?_)))
(setq org-agenda-time-grid
'((daily today require-timed)
( 0500 0600 0700 0800 0900 1000
1100 1200 1300 1400 1500 1600
1700 1800 1900 2000 2100 2200)
"" ""))
(setq org-agenda-default-appointment-duration nil)
(setq org-agenda-include-deadlines t)
(setq org-deadline-warning-days 0)
(setq org-agenda-skip-scheduled-if-done nil)
(setq org-agenda-skip-scheduled-if-deadline-is-shown t)
(setq org-agenda-skip-timestamp-if-deadline-is-shown t)
(setq org-agenda-skip-deadline-if-done nil)
(setq org-agenda-skip-deadline-prewarning-if-scheduled 1)
(setq org-agenda-skip-scheduled-delay-if-deadline nil)
(setq org-agenda-skip-additional-timestamps-same-entry nil)
(setq org-agenda-skip-timestamp-if-done nil)
(setq org-agenda-search-headline-for-time nil)
(setq org-scheduled-past-days 365)
(setq org-deadline-past-days 365)
(setq org-agenda-move-date-from-past-immediately-to-today t)
(setq org-agenda-show-future-repeats t)
(setq org-agenda-prefer-last-repeat nil)
(setq org-agenda-timerange-leaders
'("" "(%d/%d): "))
(setq org-agenda-scheduled-leaders
'("Scheduled: " "Sched.%2dx: "))
(setq org-agenda-inactive-leader "[")
(setq org-agenda-deadline-leaders
'("Deadline: " "In %3d d.: " "%2d d. ago: "))
;; Time grid
(setq org-agenda-time-leading-zero t)
(setq org-agenda-timegrid-use-ampm nil)
(setq org-agenda-use-time-grid t)
(setq org-agenda-show-current-time-in-grid t)
(setq org-agenda-current-time-string (concat "Now " (make-string 70 ?.)))
(setq org-agenda-time-grid
'((daily today require-timed)
( 0600 0700 0800 0900 1000 1100
1200 1300 1400 1500 1600 1700
1800 1900 2000 2100 2200 2300)
"" ""))
(setq org-agenda-default-appointment-duration nil)
;;;;; Agenda global to-do list
(setq org-agenda-todo-ignore-with-date t)
(setq org-agenda-todo-ignore-timestamp t)
(setq org-agenda-todo-ignore-scheduled t)
(setq org-agenda-todo-ignore-deadlines t)
(setq org-agenda-todo-ignore-time-comparison-use-seconds t)
(setq org-agenda-tags-todo-honor-ignore-options nil)
(setq org-agenda-todo-ignore-with-date t)
(setq org-agenda-todo-ignore-timestamp t)
(setq org-agenda-todo-ignore-scheduled t)
(setq org-agenda-todo-ignore-deadlines t)
(setq org-agenda-todo-ignore-time-comparison-use-seconds t)
(setq org-agenda-tags-todo-honor-ignore-options nil)
;;;;; Agenda tagged items
(setq org-agenda-show-inherited-tags t)
(setq org-agenda-use-tag-inheritance
'(todo search agenda))
(setq org-agenda-hide-tags-regexp nil)
(setq org-agenda-remove-tags nil)
(setq org-agenda-tags-column -100)
(setq org-agenda-show-inherited-tags t)
(setq org-agenda-use-tag-inheritance
'(todo search agenda))
(setq org-agenda-hide-tags-regexp nil)
(setq org-agenda-remove-tags nil)
(setq org-agenda-tags-column 1)
;;;;; Agenda entry
;; NOTE: I do not use this right now. Leaving everything to its
;; default value.
(setq org-agenda-start-with-entry-text-mode nil)
(setq org-agenda-entry-text-maxlines 5)
(setq org-agenda-entry-text-exclude-regexps nil)
(setq org-agenda-entry-text-leaders " > ")
;; NOTE: I do not use this right now. Leaving everything to its
;; default value.
(setq org-agenda-start-with-entry-text-mode nil)
(setq org-agenda-entry-text-maxlines 5)
(setq org-agenda-entry-text-exclude-regexps nil)
(setq org-agenda-entry-text-leaders " > ")
;;;;; Agenda logging and clocking
;; NOTE: I do not use these yet, though I plan to. Leaving everything
;; to its default value for the time being.
(setq org-agenda-log-mode-items '(closed clock))
(setq org-agenda-clock-consistency-checks
'((:max-duration "10:00" :min-duration 0 :max-gap "0:05" :gap-ok-around
("4:00")
:default-face ; This should definitely be reviewed
((:background "DarkRed")
(:foreground "white"))
:overlap-face nil :gap-face nil :no-end-time-face nil
:long-face nil :short-face nil)))
(setq org-agenda-log-mode-add-notes t)
(setq org-agenda-start-with-log-mode nil)
(setq org-agenda-start-with-clockreport-mode nil)
(setq org-agenda-clockreport-parameter-plist '(:link t :maxlevel 2))
(setq org-agenda-search-view-always-boolean nil)
(setq org-agenda-search-view-force-full-words nil)
(setq org-agenda-search-view-max-outline-level 0)
(setq org-agenda-search-headline-for-time t)
(setq org-agenda-use-time-grid t)
(setq org-agenda-cmp-user-defined nil)
(setq org-agenda-sort-notime-is-late t) ; Org 9.4
(setq org-agenda-sort-noeffort-is-high t) ; Org 9.4
;; NOTE: I do not use these yet, though I plan to. Leaving everything
;; to its default value for the time being.
(setq org-agenda-log-mode-items '(closed clock))
(setq org-agenda-clock-consistency-checks
'((:max-duration "10:00" :min-duration 0 :max-gap "0:05" :gap-ok-around
("4:00")
:default-face ; This should definitely be reviewed
((:background "DarkRed")
(:foreground "white"))
:overlap-face nil :gap-face nil :no-end-time-face nil
:long-face nil :short-face nil)))
(setq org-agenda-log-mode-add-notes t)
(setq org-agenda-start-with-log-mode nil)
(setq org-agenda-start-with-clockreport-mode nil)
(setq org-agenda-clockreport-parameter-plist '(:link t :maxlevel 2))
(setq org-agenda-search-view-always-boolean nil)
(setq org-agenda-search-view-force-full-words nil)
(setq org-agenda-search-view-max-outline-level 0)
(setq org-agenda-search-headline-for-time t)
(setq org-agenda-use-time-grid t)
(setq org-agenda-cmp-user-defined nil)
(setq org-agenda-sort-notime-is-late t) ; Org 9.4
(setq org-agenda-sort-noeffort-is-high t) ; Org 9.4
;; ;;;;; Agenda habits
;; (require 'org-habit)
;; (setq org-habit-graph-column 50)
;; (setq org-habit-preceding-days 9)
;; ;; Always show the habit graph, even if there are no habits for
;; ;; today.
;; (setq org-habit-show-all-today t)
;;;;; Agenda column view
;; NOTE I do not use these, but may need them in the future.
(setq org-agenda-view-columns-initially nil)
(setq org-agenda-columns-show-summaries t)
(setq org-agenda-columns-compute-summary-properties t)
(setq org-agenda-columns-add-appointments-to-effort-sum nil)
(setq org-agenda-auto-exclude-function nil)
(setq org-agenda-bulk-custom-functions nil)
;; ;;;;; Agenda habits
;; (require 'org-habit)
;; (setq org-habit-graph-column 50)
;; (setq org-habit-preceding-days 9)
;; ;; Always show the habit graph, even if there are no habits for
;; ;; today.
;; (setq org-habit-show-all-today t)
)
;; NOTE I do not use these, but may need them in the future.
(setq org-agenda-view-columns-initially nil)
(setq org-agenda-columns-show-summaries t)
(setq org-agenda-columns-compute-summary-properties t)
(setq org-agenda-columns-add-appointments-to-effort-sum nil)
(setq org-agenda-auto-exclude-function nil)
(setq org-agenda-bulk-custom-functions nil)))
(provide 'prot-emacs-org)

View file

@ -1,136 +1,122 @@
;;; Isearch, occur, grep, and extras (prot-search.el)
(use-package isearch
:ensure nil
:demand t
:config
(setq search-whitespace-regexp ".*?" ; one `setq' here to make it obvious they are a bundle
isearch-lax-whitespace t
isearch-regexp-lax-whitespace nil))
(prot-emacs-configure
(setq search-whitespace-regexp ".*?")
(setq isearch-lax-whitespace t)
(setq isearch-regexp-lax-whitespace nil))
(use-package isearch
:ensure nil
:demand t
:config
(prot-emacs-configure
(setq search-highlight t)
(setq isearch-lazy-highlight t)
(setq lazy-highlight-initial-delay 0.5)
(setq lazy-highlight-no-delay-length 4))
(use-package isearch
:ensure nil
:demand t
:config
(prot-emacs-configure
(setq isearch-lazy-count t)
(setq lazy-count-prefix-format "(%s/%s) ")
(setq lazy-count-suffix-format nil))
(use-package isearch
:ensure nil
:demand t
:config
(prot-emacs-configure
(setq isearch-wrap-pause t) ; `no-ding' makes keyboard macros never quit
(setq isearch-repeat-on-direction-change t))
(use-package isearch
:ensure nil
:demand t
:config
(setq list-matching-lines-jump-to-current-line nil) ; do not jump to current line in `*occur*' buffers
(add-hook 'occur-mode-hook #'prot-common-truncate-lines-silently) ; from `prot-common.el'
(add-hook 'occur-mode-hook #'hl-line-mode))
(prot-emacs-configure
(setq query-replace-from-to-separator " > ")
(defun prot/query-replace-preview (match replacement)
"Do the work of `query-replace-show-preview'."
(format "%s%s%s"
(propertize match 'face 'query-replace-preview-match)
(propertize query-replace-from-to-separator 'face 'secondary-selection)
(propertize replacement 'face 'query-replace-preview)))
(setq query-replace-show-preview #'prot/query-replace-preview)) ; Emacs 32
(use-package isearch
:ensure nil
:demand t
:bind
( :map minibuffer-local-isearch-map
("M-/" . isearch-complete-edit)
:map occur-mode-map
("t" . toggle-truncate-lines)
:map isearch-mode-map
("C-g" . isearch-cancel) ; instead of `isearch-abort'
("M-/" . isearch-complete)))
(prot-emacs-configure
(setq list-matching-lines-jump-to-current-line nil) ; do not jump to current line in `*occur*' buffers
(prot-emacs-hook occur-mode-hook (prot-common-truncate-lines-silently hl-line-mode)))
(use-package prot-search
:ensure nil
:bind
( :map global-map
("M-s M-%" . prot-search-replace-markup) ; see `prot-search-markup-replacements'
("M-s M-<" . prot-search-isearch-beginning-of-buffer)
("M-s M->" . prot-search-isearch-end-of-buffer)
("M-s g" . prot-search-grep)
("M-s u" . prot-search-occur-urls)
("M-s t" . prot-search-occur-todo-keywords)
("M-s M-t" . prot-search-grep-todo-keywords) ; With C-u it runs `prot-search-git-grep-todo-keywords'
("M-s M-T" . prot-search-git-grep-todo-keywords)
("M-s s" . prot-search-outline)
("M-s M-s" . prot-search-outline)
("M-s M-o" . prot-search-occur-outline)
("M-s M-u" . prot-search-occur-browse-url)
:map isearch-mode-map
("<up>" . prot-search-isearch-repeat-backward)
("<down>" . prot-search-isearch-repeat-forward)
("<backspace>" . prot-search-isearch-abort-dwim)
("<C-return>" . prot-search-isearch-other-end))
:config
(setq prot-search-outline-regexp-alist
'((emacs-lisp-mode . "^\\((\\|;;;+ \\)")
(org-mode . "^\\(\\*+ +\\|#\\+[Tt][Ii][Tt][Ll][Ee]:\\)")
(outline-mode . "^\\*+ +")
(emacs-news-view-mode . "^\\*+ +")
(conf-toml-mode . "^\\[")
(markdown-mode . "^#+ +")))
(setq prot-search-todo-keywords
(concat "TODO\\|FIXME\\|NOTE\\|REVIEW\\|XXX\\|KLUDGE"
"\\|HACK\\|WARN\\|WARNING\\|DEPRECATED\\|BUG"))
(prot-emacs-configure
(define-key global-map (kbd "C-.") #'isearch-forward-symbol-at-point) ; easier than M-s . // I also have `prot-simple-mark-sexp' on C-,
(define-key minibuffer-local-isearch-map (kbd "M-/") #'isearch-complete-edit)
(define-key occur-mode-map (kbd "t") #'toggle-truncate-lines)
(prot-emacs-keybind isearch-mode-map
"C-g" #'isearch-cancel ; instead of `isearch-abort'
"M-/" #'isearch-complete))
(with-eval-after-load 'pulsar
(add-hook 'prot-search-outline-hook #'pulsar-recenter-center)
(add-hook 'prot-search-outline-hook #'pulsar-reveal-entry)))
(prot-emacs-configure
(require 'prot-search)
(prot-emacs-keybind global-map
"M-s M-%" #'prot-search-replace-markup ; see `prot-search-markup-replacements'
"M-s g" #'prot-search-grep
"M-s u" #'prot-search-occur-urls
"M-s t" #'prot-search-occur-todo-keywords
"M-s M-t" #'prot-search-grep-todo-keywords ; With C-u it runs `prot-search-git-grep-todo-keywords'
"M-s M-T" #'prot-search-git-grep-todo-keywords
"M-s s" #'prot-search-outline
"M-s O" #'prot-search-collect
"M-s M-o" #'prot-search-occur-outline
"M-s M-u" #'prot-search-occur-browse-url)
(prot-emacs-keybind isearch-mode-map
"C-<home>" #'isearch-beginning-of-buffer
"C-<end>" #'isearch-end-of-buffer
"<up>" #'isearch-repeat-backward
"<down>" #'isearch-repeat-forward
"<backspace>" #'prot-search-isearch-abort-dwim
"<C-return>" #'prot-search-isearch-other-end)
(setq prot-search-outline-regexp-alist
'((emacs-lisp-mode . "^\\((\\|;;;+ \\)")
(org-mode . "^\\(\\*+ +\\|#\\+[Tt][Ii][Tt][Ll][Ee]:\\)")
(outline-mode . "^\\*+ +")
(emacs-news-view-mode . "^\\*+ +")
(conf-toml-mode . "^\\[")
(markdown-mode . "^#+ +")))
(setq prot-search-todo-keywords
(concat "TODO\\|FIXME\\|NOTE\\|REVIEW\\|XXX\\|KLUDGE"
"\\|HACK\\|WARN\\|WARNING\\|DEPRECATED\\|BUG"))
(prot-emacs-hook
prot-search-outline-hook
(pulsar-recenter-center pulsar-reveal-entry)
nil
pulsar))
;;; grep and xref
(use-package re-builder
:ensure nil
:commands (re-builder regexp-builder)
:config
(setq reb-re-syntax 'read))
(prot-emacs-configure
(setq reb-re-syntax 'read)
(setq xref-file-name-display 'project-relative)
(use-package xref
:ensure nil
:commands (xref-find-definitions xref-go-back)
:config
;; All those have been changed for Emacs 28
(setq xref-show-definitions-function #'xref-show-definitions-completing-read) ; for M-.
(setq xref-show-xrefs-function #'xref-show-definitions-buffer) ; for grep and the like
(setq xref-file-name-display 'project-relative))
(use-package grep
:ensure nil
:commands (grep lgrep rgrep)
:config
;; The `outline-xref' is for Emacs 32
(with-eval-after-load 'outline
(define-advice outline-xref (:around (&rest args) prot)
(let ((xref-show-xrefs-function #'xref-show-definitions-completing-read))
(apply args))))
(setq grep-save-buffers nil)
(setq grep-use-headings t) ; Emacs 30
(setq grep-use-headings nil) ; Emacs 30
(let ((executable (or (executable-find "rg") "grep"))
(rgp (string-match-p "rg" grep-program)))
(setq grep-program executable)
(setq grep-template
(if rgp
"/usr/bin/rg -nH --null -e <R> <F>"
"/usr/bin/grep <X> <C> -nH --null -e <R> <F>"))
(setq xref-search-program (if rgp 'ripgrep 'grep))))
(if-let* ((ripgrep (or (executable-find "rg") (executable-find "ripgrep"))))
(progn
(setq grep-program ripgrep)
(setq xref-search-program 'ripgrep)
(setq grep-template (format "%s -nH --null -e <R> <F>" ripgrep)))
(error "You need to install RIPGREP"))
(add-hook 'grep-mode #'prot-common-truncate-lines-silently))
;;; wgrep (writable grep)
(use-package wgrep
:ensure t
:after grep
:bind
( :map grep-mode-map
("e" . wgrep-change-to-wgrep-mode)
("C-x C-q" . wgrep-change-to-wgrep-mode)
("C-c C-c" . wgrep-finish-edit))
:config
(setq wgrep-auto-save-buffer t)
(setq wgrep-change-readonly-file t))
;; See the `grep-edit-mode' for the new built-in feature.
(unless (>= emacs-major-version 31)
(prot-emacs-configure
(prot-emacs-install wgrep)
(with-eval-after-load 'grep
(prot-emacs-keybind grep-mode-map
"e" #'wgrep-change-to-wgrep-mode
"C-x C-q" #'wgrep-change-to-wgrep-mode
"C-c C-c" #'wgrep-finish-edit)
(setq wgrep-auto-save-buffer t)
(setq wgrep-change-readonly-file t))))
(provide 'prot-emacs-search)

View file

@ -1,102 +1,59 @@
;;; The Standard themes
;; The themes are customisable. Read the manual:
;; <https://protesilaos.com/emacs/standard-themes>.
(prot-emacs-configure
(prot-emacs-install standard-themes)
(use-package standard-themes
:ensure t
:demand t
:bind ("<f5>" . standard-themes-toggle)
:config
(setq standard-themes-bold-constructs t
standard-themes-italic-constructs t
standard-themes-mixed-fonts t
standard-themes-variable-pitch-ui t
standard-themes-mode-line-accented nil
(standard-themes-take-over-modus-themes-mode 1)
;; Accepts a symbol value
standard-themes-fringes 'subtle
(prot-emacs-keybind global-map
"<f5>" #'modus-themes-rotate
"C-<f5>" #'modus-themes-select
"M-<f5>" #'modus-themes-load-random)
;; The following accept lists of properties
standard-themes-links nil
standard-themes-region nil
standard-themes-prompts nil
;; more complex alist to set weight, height, and optional
;; `variable-pitch' per heading level (t is for any level not
;; specified)
standard-themes-headings
(setq modus-themes-variable-pitch-ui t
modus-themes-mixed-fonts t
modus-themes-to-rotate nil ; defaults to the return value of `modus-themes-get-themes'
modus-themes-headings ; read the manual's entry of the doc string
'((0 . (variable-pitch light 1.9))
(1 . (variable-pitch light 1.8))
(2 . (variable-pitch light 1.7))
(3 . (variable-pitch semilight 1.6))
(4 . (variable-pitch semilight 1.5))
(5 . (variable-pitch 1.4))
(2 . (variable-pitch regular 1.7))
(3 . (variable-pitch regular 1.6))
(4 . (variable-pitch regular 1.5))
(5 . (variable-pitch 1.4)) ; absence of weight means `bold'
(6 . (variable-pitch 1.3))
(7 . (variable-pitch 1.2))
(agenda-date . (1.3))
(agenda-structure . (variable-pitch light 1.8))
(agenda-date . (semilight 1.5))
(agenda-structure . (variable-pitch light 1.9))
(t . (variable-pitch 1.1))))
;; Load a theme that is consistent with my session's theme. Those
;; functions are defined in my init.el.
(if (prot-emacs-theme-environment-dark-p)
(standard-themes-load-dark)
(standard-themes-load-light)))
;; The `standard-themes' provide lots of themes. I want to pick one at
;; random when I start Emacs: the `modus-themes-load-random' does just
;; that (it can be called interactively as well). I just check with
;; my desktop environment to determine if the choice should be about
;; a light or a dark theme. Those functions are in my init.el.
(if (prot-emacs-gnome-prefers-dark-p)
(modus-themes-load-random 'dark)
(modus-themes-load-random 'light)))
;; NOTE: For testing purposes
;; For testing purposes...
(prot-emacs-comment
(:eval nil)
(progn
(mapc #'disable-theme custom-enabled-themes)
(add-to-list 'load-path "/home/prot/Git/Projects/standard-themes/")
(add-to-list 'load-path "~/Git/Projects/modus-themes/")
(add-to-list 'load-path "~/Git/Projects/standard-themes/")
(require 'standard-themes)
(load-theme 'standard-dark t t)
(load-theme 'standard-light t t)
(require 'modus-themes)
(require 'standard-themes)
(setq standard-themes-bold-constructs t
standard-themes-italic-constructs t
standard-themes-disable-other-themes t
standard-themes-mixed-fonts t
standard-themes-variable-pitch-ui t
standard-themes-mode-line-accented nil
(standard-themes-take-over-modus-themes-mode 1)
;; Accepts a symbol value
standard-themes-fringes 'subtle
(prot-emacs-keybind global-map
"<f5>" #'modus-themes-rotate
"C-<f5>" #'modus-themes-select
"M-<f5>" #'modus-themes-load-random)
;; The following accept lists of properties
standard-themes-links nil
standard-themes-region nil
standard-themes-prompts nil
;; more complex alist to set weight, height, and optional
;; `variable-pitch' per heading level (t is for any level not
;; specified)
standard-themes-headings
'((0 . (variable-pitch light 1.9))
(1 . (variable-pitch light 1.8))
(2 . (variable-pitch light 1.7))
(3 . (variable-pitch semilight 1.6))
(4 . (variable-pitch semilight 1.5))
(5 . (variable-pitch 1.4))
(6 . (variable-pitch 1.3))
(7 . (variable-pitch 1.2))
(agenda-date . (1.3))
(agenda-structure . (variable-pitch light 1.8))
(t . (variable-pitch 1.1))))
(mapcar (lambda (theme)
(add-to-list
'custom-theme-load-path
(concat "/home/prot/Git/Projects/standard-themes/" (symbol-name theme) "-theme.el")))
(standard-themes--list-enabled-themes))
(if (prot-emacs-theme-environment-dark-p)
(load-theme 'standard-dark :no-confirm)
(load-theme 'standard-light :no-confirm))
(define-key global-map (kbd "<f5>") #'standard-themes-toggle)))
(if (prot-emacs-gnome-prefers-dark-p)
(modus-themes-load-random 'dark)
(modus-themes-load-random 'light)))
(provide 'prot-emacs-standard-themes)

View file

@ -2,305 +2,333 @@
;;;; Load the desired theme module
;; These all reference my packages: `modus-themes', `ef-themes',
;; `standard-themes'.
;; `doric-themes', `standard-themes'.
(when prot-emacs-load-theme-family
(require
(pcase prot-emacs-load-theme-family
('ef 'prot-emacs-ef-themes)
('modus 'prot-emacs-modus-themes)
('ef 'prot-emacs-ef-themes)
('doric 'prot-emacs-doric-themes)
('standard 'prot-emacs-standard-themes))))
;;;; Pulsar
;; Read the pulsar manual: <https://protesilaos.com/emacs/pulsar>.
(use-package pulsar
:ensure t
:config
(setopt pulsar-pulse t
pulsar-delay 0.055
pulsar-iterations 10
pulsar-face 'pulsar-magenta
pulsar-highlight-face 'pulsar-cyan)
(prot-emacs-configure
(prot-emacs-install pulsar)
(pulsar-global-mode 1)
:hook
;; There are convenience functions/commands which pulse the line using
;; a specific colour: `pulsar-pulse-line-red' is one of them.
((next-error . (pulsar-pulse-line-red pulsar-recenter-top pulsar-reveal-entry))
(minibuffer-setup . pulsar-pulse-line-red))
:bind
;; pulsar does not define any key bindings. This is just my personal
;; preference. Remember to read the manual on the matter. Evaluate:
;;
;; (info "(elisp) Key Binding Conventions")
(("C-x l" . pulsar-pulse-line) ; override `count-lines-page'
("C-x L" . pulsar-highlight-dwim))) ; or use `pulsar-highlight-line'
(prot-emacs-hook
(next-error-hook minibuffer-setup-hook)
(pulsar-pulse-line-red pulsar-recenter-top pulsar-reveal-entry))
(setq pulsar-delay 0.055)
(setq pulsar-iterations 5)
(setq pulsar-face 'pulsar-green)
(setq pulsar-region-face 'pulsar-yellow)
(setq pulsar-highlight-face 'pulsar-magenta)
(prot-emacs-keybind global-map
"C-x l" #'pulsar-pulse-line ; override `count-lines-page'
"C-x L" #'pulsar-highlight-permanently-dwim)) ; or use `pulsar-highlight-temporarily-dwim'
;;;; Lin
;; Read the lin manual: <https://protesilaos.com/emacs/lin>.
(use-package lin
:ensure t
:hook (after-init . lin-global-mode) ; applies to all `lin-mode-hooks'
:config
;; You can use this to live update the face:
;;
;; (customize-set-variable 'lin-face 'lin-green)
;;
;; Or `setopt' on Emacs 29: (setopt lin-face 'lin-yellow)
;;
;; I still prefer `setq' for consistency.
(setq lin-face 'lin-magenta))
(prot-emacs-configure
(prot-emacs-install lin)
(setq lin-face 'lin-slate)
(lin-global-mode 1)
(when (string= (getenv "DESKTOP_SESSION") "gnome")
(lin-gnome-accent-color-mode 1)))
;;;; Increase padding of windows/frames
;; Yet another one of my packages:
;; <https://protesilaos.com/codelog/2023-06-03-emacs-spacious-padding/>.
(use-package spacious-padding
:ensure t
:if (display-graphic-p)
:hook (after-init . spacious-padding-mode)
:bind ("<f8>" . spacious-padding-mode)
:init
;; These are the defaults, but I keep it here for visiibility.
(setq spacious-padding-widths
'( :internal-border-width 15
:header-line-width 4
:mode-line-width 6
:tab-width 4
:right-divider-width 1
:scroll-bar-width 8
:left-fringe-width 20
:right-fringe-width 20))
(when prot-display-graphic-p
(prot-emacs-configure
(prot-emacs-install spacious-padding)
;; ;; Read the doc string of `spacious-padding-subtle-mode-line' as
;; ;; it is very flexible.
;;
;; (setq spacious-padding-subtle-mode-line
;; `( :mode-line-active ,(if (or (eq prot-emacs-load-theme-family 'modus)
;; (eq prot-emacs-load-theme-family 'standard))
;; 'default
;; 'help-key-binding)
;; :mode-line-inactive vertical-border))
(spacious-padding-mode 1)
(setq spacious-padding-subtle-mode-line nil))
(define-key global-map (kbd "<f8>") #'spacious-padding-mode)
;;;; Rainbow mode for colour previewing (rainbow-mode.el)
(use-package rainbow-mode
:ensure t
:init
(setq rainbow-ansi-colors nil)
(setq rainbow-x-colors nil)
(setq spacious-padding-widths
`( :internal-border-width 15
:header-line-width 4
:mode-line-width 6
:tab-width 4
:right-divider-width 15
:scroll-bar-width ,(if prot-pgtk-p 12 6)
:left-fringe-width 8
:right-fringe-width 20))
(defun prot/rainbow-mode-in-themes ()
(when-let ((file (buffer-file-name))
((derived-mode-p 'emacs-lisp-mode))
((string-match-p "-theme" file)))
(rainbow-mode 1)))
:bind ( :map ctl-x-x-map
("c" . rainbow-mode)) ; C-x x c
:hook (emacs-lisp-mode . prot/rainbow-mode-in-themes))
;; (setq spacious-padding-subtle-mode-line nil)
;; Read the doc string of `spacious-padding-subtle-mode-line' as it
;; is very flexible. Here we make the mode lines be a single
;; overline, while header lines have an underline.
(setq spacious-padding-subtle-frame-lines
'( :mode-line-active spacious-padding-line-active
:mode-line-inactive spacious-padding-line-inactive
:header-line-active spacious-padding-line-active
:header-line-inactive spacious-padding-line-inactive))
(when (< emacs-major-version 29)
(setq x-underline-at-descent-line (when spacious-padding-subtle-frame-lines t)))))
;;; Cursor appearance (cursory)
;; Read the manual: <https://protesilaos.com/emacs/cursory>.
(use-package cursory
:ensure t
:demand t
:if (display-graphic-p)
:config
(setq cursory-presets
'((box
:blink-cursor-interval 1.2)
(box-no-blink
:blink-cursor-mode -1)
(bar
:cursor-type (bar . 2)
:blink-cursor-interval 0.5)
(bar-no-other-window
:inherit bar
:cursor-in-non-selected-windows nil)
(underscore
:cursor-type (hbar . 3)
:blink-cursor-blinks 50)
(underscore-thin-other-window
:inherit underscore
:cursor-in-non-selected-windows (hbar . 1))
(underscore-thick
:cursor-type (hbar . 8)
:blink-cursor-interval 0.3
:blink-cursor-blinks 50
:cursor-in-non-selected-windows (hbar . 3))
(underscore-thick-no-blink
:blink-cursor-mode -1
:cursor-type (hbar . 8)
:cursor-in-non-selected-windows (hbar . 3))
(t ; the default values
:cursor-type box
:cursor-in-non-selected-windows hollow
:blink-cursor-mode 1
:blink-cursor-blinks 10
:blink-cursor-interval 0.2
:blink-cursor-delay 0.2)))
(when prot-display-graphic-p
(prot-emacs-configure
(prot-emacs-install cursory)
;; I am using the default values of `cursory-latest-state-file'.
(require 'cursory)
;; Set last preset or fall back to desired style from `cursory-presets'.
(cursory-set-preset (or (cursory-restore-latest-preset) 'box))
:hook
;; The other side of `cursory-restore-latest-preset'.
(kill-emacs . cursory-store-latest-preset)
:bind
;; We have to use the "point" mnemonic, because C-c c is often the
;; suggested binding for `org-capture' and is the one I use as well.
("C-c p" . cursory-set-preset))
(setq cursory-presets
'((box
:blink-cursor-interval 1.2)
(box-no-blink
:inherit box
:blink-cursor-mode -1)
(bar
:cursor-type (bar . 2)
:cursor-color error ; will typically be red
:blink-cursor-interval 0.8)
(bar-no-other-window
:inherit bar
:cursor-in-non-selected-windows nil)
(bar-no-blink
:inherit bar
:blink-cursor-mode -1)
(underscore
:cursor-color warning ; will typically be yellow
:cursor-type (hbar . 3)
:blink-cursor-blinks 50)
(underscore-no-other-window
:inherit underscore
:cursor-in-non-selected-windows nil)
(underscore-thick
:inherit underscore
:cursor-type (hbar . 8)
:cursor-in-non-selected-windows (hbar . 3))
(t ; the default values
:cursor-color unspecified ; use the theme's original
:cursor-type box
:cursor-in-non-selected-windows hollow
:blink-cursor-mode 1
:blink-cursor-blinks 10
:blink-cursor-interval 0.2
:blink-cursor-delay 0.2)))
(cursory-set-last-or-fallback)
;; Persist configurations between Emacs sessions. Also apply the
;; :cursor-color again when swithcing to another theme.
(cursory-mode 1)
;; We have to use the "point" mnemonic, because C-c c is often the
;; suggested binding for `org-capture' and is the one I use as well.
(define-key global-map (kbd "C-c p") #'cursory-set-preset)
(with-eval-after-load 'prot-streaming
(defvar prot/cursory-streaming--last-preset nil
"The last Cursory preset before enabling `prot/cursory-streaming-mode'.")
(defun prot/cursory-streaming-mode ()
"Set the Cursory preset to `box-no-blink' when streaming."
(cond
(prot-streaming-mode
(setq prot/cursory-streaming-mode cursory-last-selected-preset)
(cursory-set-preset 'box-no-blink))
(prot/cursory-streaming-mode
(cursory-set-preset prot/cursory-streaming-mode)
(setq prot/cursory-streaming-mode nil))
(t
(cursory-set-last-or-fallback))))
(add-hook 'prot-streaming-mode-hook #'prot/cursory-streaming-mode))))
;;; GNOME accent theme switcher
(when (and (null prot-emacs-load-theme-family)
(string= (getenv "DESKTOP_SESSION") "gnome"))
(prot-emacs-configure
(prot-emacs-install-vc
gnome-accent-theme-switcher
"https://github.com/protesilaos/gnome-accent-theme-switcher.git")
(prot-emacs-keybind global-map
"<f5>" #'gnome-accent-theme-switcher-toggle-mode
"C-<f5>" #'gnome-accent-theme-switcher-change-accent
"M-<f5>" #'gnome-accent-theme-switcher-load-random-from-current-set)
(gnome-accent-theme-switcher-mode 1)))
;;; Modus themes exporter
(prot-emacs-configure
;; The use the command `modus-themes-exporter-export'.
(prot-emacs-install-vc
modus-themes-exporter
"https://github.com/protesilaos/modus-themes-exporter.git"))
;;;; Theme buffet
(use-package theme-buffet
:ensure t
:config
(prot-emacs-configure
(prot-emacs-install theme-buffet)
(setq theme-buffet-menu 'end-user)
(setq theme-buffet--end-user
(setq theme-buffet-end-user
'( :night (modus-vivendi ef-dark ef-winter ef-autumn ef-night ef-duo-dark ef-symbiosis)
:morning (modus-operandi ef-light ef-cyprus ef-spring ef-frost ef-duo-light)
:afternoon (modus-operandi-tinted ef-arbutus ef-day ef-kassio ef-summer ef-elea-light ef-maris-light ef-melissa-light ef-trio-light)
:evening (modus-vivendi-tinted ef-rosa ef-elea-dark ef-maris-dark ef-melissa-dark ef-trio-dark)))
:afternoon (modus-operandi-tinted ef-arbutus ef-day ef-kassio ef-summer ef-elea-light ef-maris-light ef-melissa-light ef-trio-light ef-reverie)
:evening (modus-vivendi-tinted ef-rosa ef-elea-dark ef-maris-dark ef-melissa-dark ef-trio-dark ef-dream)))
(theme-buffet-timer-hours 1))
;;;; Fontaine (font configurations)
;; Read the manual: <https://protesilaos.com/emacs/fontaine>
(use-package fontaine
:ensure t
:demand t
:if (display-graphic-p)
:bind ("C-c f" . fontaine-set-preset)
:config
;; This is defined in Emacs C code: it belongs to font settings.
(setq x-underline-at-descent-line nil)
(prot-emacs-configure
(prot-emacs-install fontaine)
;; And this is for Emacs28.
(setq-default text-scale-remap-header-line t)
(fontaine-mode 1)
;; This is the default value. Just including it here for
;; completeness.
(setq fontaine-latest-state-file (locate-user-emacs-file "fontaine-latest-state.eld"))
(prot-emacs-keybind global-map
"C-c f" #'fontaine-set-preset
"C-c F" #'fontaine-toggle-preset)
(setq-default text-scale-remap-header-line t) ; Emacs 28
;; The font family is my design: <https://github.com/protesilaos/aporetic>.
(setq fontaine-presets
'((small
:default-family "Iosevka Comfy Motion"
:default-height 80
:variable-pitch-family "Iosevka Comfy Duo")
:default-height 80)
(regular) ; like this it uses all the fallback values and is named `regular'
(medium
:default-weight semilight
:default-family "Aporetic Serif Mono"
:default-height 115
:bold-weight extrabold)
:fixed-pitch-family "Aporetic Serif Mono"
:variable-pitch-family "Aporetic Sans")
(large
:inherit medium
:default-height 150)
(live-stream
:default-family "Iosevka Comfy Wide Motion"
:default-height 150
:default-weight medium
:fixed-pitch-family "Iosevka Comfy Wide Motion"
:variable-pitch-family "Iosevka Comfy Wide Duo"
:bold-weight extrabold)
(presentation
:default-height 180)
:default-height 220)
(jumbo
:inherit medium
:default-height 280)
(t
;; I keep all properties for didactic purposes, but most can be
;; omitted. See the fontaine manual for the technicalities:
;; <https://protesilaos.com/emacs/fontaine>.
:default-family "Iosevka Comfy"
:default-family "Aporetic Sans Mono"
:default-weight regular
:default-slant normal
:default-width normal
:default-height 100
:fixed-pitch-family "Iosevka Comfy"
:fixed-pitch-family "Aporetic Sans Mono"
:fixed-pitch-weight nil
:fixed-pitch-slant nil
:fixed-pitch-width nil
:fixed-pitch-height 1.0
:fixed-pitch-serif-family nil
:fixed-pitch-serif-weight nil
:fixed-pitch-serif-slant nil
:fixed-pitch-serif-width nil
:fixed-pitch-serif-height 1.0
:variable-pitch-family "Iosevka Comfy Motion Duo"
:variable-pitch-family "Aporetic Serif"
:variable-pitch-weight nil
:variable-pitch-slant nil
:variable-pitch-width nil
:variable-pitch-height 1.0
:mode-line-active-family nil
:mode-line-active-weight nil
:mode-line-active-slant nil
:mode-line-active-width nil
:mode-line-active-height 1.0
:mode-line-inactive-family nil
:mode-line-inactive-weight nil
:mode-line-inactive-slant nil
:mode-line-inactive-width nil
:mode-line-inactive-height 1.0
:header-line-family nil
:header-line-weight nil
:header-line-slant nil
:header-line-width nil
:header-line-height 1.0
:line-number-family nil
:line-number-weight nil
:line-number-slant nil
:line-number-width nil
:line-number-height 1.0
:tab-bar-family nil
:tab-bar-weight nil
:tab-bar-slant nil
:tab-bar-width nil
:tab-bar-height 1.0
:tab-line-family nil
:tab-line-weight nil
:tab-line-slant nil
:tab-line-width nil
:tab-line-height 1.0
:bold-family nil
:bold-weight bold
:bold-slant nil
:bold-weight bold
:bold-width nil
:bold-height 1.0
:italic-family nil
:italic-weight nil
:italic-slant italic
:italic-width nil
:italic-height 1.0
:line-spacing nil)))
;; Set last preset or fall back to desired style from `fontaine-presets'.
(fontaine-set-preset (or (fontaine-restore-latest-preset) 'regular))
;; Persist the latest font preset when closing/starting Emacs and
;; while switching between themes.
(fontaine-mode 1)
(with-eval-after-load 'pulsar
(add-hook 'fontaine-set-preset-hook #'pulsar-pulse-line)))
;;;; Show Font (preview fonts)
;; Read the manual: <https://protesilaos.com/emacs/show-font>
(when prot-display-graphic-p
(prot-emacs-configure
(prot-emacs-install show-font)
(setq show-font-display-buffer-action-alist '(display-buffer-full-frame))
(prot-emacs-keybind global-map
"C-c S s" #'show-font-select-preview
"C-c S l" #'show-font-tabulated)))
;;;;; `variable-pitch-mode' setup
(use-package face-remap
:ensure nil
:functions prot/enable-variable-pitch
:bind ( :map ctl-x-x-map
("v" . variable-pitch-mode))
:hook ((text-mode notmuch-show-mode elfeed-show-mode) . prot/enable-variable-pitch)
:config
;; NOTE 2022-11-20: This may not cover every case, though it works
;; fine in my workflow. I am still undecided by EWW.
(prot-emacs-configure
(define-key ctl-x-x-map (kbd "v") #'variable-pitch-mode)
(defun prot/enable-variable-pitch ()
(unless (derived-mode-p 'mhtml-mode 'nxml-mode 'yaml-mode)
(variable-pitch-mode 1)))
(when (bound-and-true-p modus-themes-mixed-fonts)
(variable-pitch-mode 1))))
;; NOTE 2022-11-20: This may not cover every case, though it works
;; fine in my workflow. I am still undecided by EWW.
(prot-emacs-hook
(text-mode-hook notmuch-show-mode-hook elfeed-show-mode-hook)
prot/enable-variable-pitch)
;;;;; Resize keys with global effect
:bind
;; Emacs 29 introduces commands that resize the font across all
;; buffers (including the minibuffer), which is what I want, as
;; opposed to doing it only in the current buffer. The keys are the
;; same as the defaults.
(("C-x C-=" . global-text-scale-adjust)
("C-x C-+" . global-text-scale-adjust)
("C-x C-0" . global-text-scale-adjust)))
(prot-emacs-keybind global-map
"C-x C-=" #'global-text-scale-adjust
"C-x C-+" #'global-text-scale-adjust
"C-x C-0" #'global-text-scale-adjust))
(provide 'prot-emacs-theme)

View file

@ -1,10 +1,9 @@
;;; Vertical completion layout (vertico)
(use-package vertico
:ensure t
:hook (after-init . vertico-mode)
:config
(prot-emacs-configure
(prot-emacs-install vertico)
(setq vertico-scroll-margin 0)
(setq vertico-count 5)
(setq vertico-count 10)
(setq vertico-resize t)
(setq vertico-cycle t)
@ -13,42 +12,55 @@
;; a sub-directory and use, say, `find-file' to go to your home '~/'
;; or root '/' directory, Vertico will clear the old path to keep
;; only your current input.
(add-hook 'rfn-eshadow-update-overlay-hook #'vertico-directory-tidy)))
(add-hook 'rfn-eshadow-update-overlay-hook #'vertico-directory-tidy))
(setq vertico-group-format
(concat
(propertize (make-string 20 ? ) 'face 'completions-group-separator)
(propertize " %s " 'face 'completions-group-title)
(propertize " " 'face 'completions-group-separator 'display '(space :align-to right))))
(vertico-mode 1))
;;; Custom tweaks for vertico (prot-vertico.el)
(use-package prot-vertico
:ensure nil
:demand t
:after vertico
:bind
( :map vertico-map
("<left>" . backward-char)
("<right>" . forward-char)
("TAB" . prot-vertico-private-complete)
("DEL" . vertico-directory-delete-char)
("M-DEL" . vertico-directory-delete-word)
("M-," . vertico-quick-insert)
("M-." . vertico-quick-exit)
:map vertico-multiform-map
("C-n" . prot-vertico-private-next)
("<down>" . prot-vertico-private-next)
("C-p" . prot-vertico-private-previous)
("<up>" . prot-vertico-private-previous)
("C-l" . vertico-multiform-vertical))
:config
(prot-emacs-configure
(require 'prot-vertico)
(setq vertico-reverse-candidates nil)
(setq vertico-multiform-commands
`(("consult-\\(.*\\)?\\(find\\|grep\\|ripgrep\\)" ,@prot-vertico-multiform-maximal)))
(setq vertico-multiform-categories
`(;; Maximal
(embark-keybinding ,@prot-vertico-multiform-maximal)
(multi-category ,@prot-vertico-multiform-maximal)
(consult-location ,@prot-vertico-multiform-maximal)
(jinx ,@prot-vertico-multiform-maximal)
(imenu ,@prot-vertico-multiform-maximal)
(theme ,@prot-vertico-multiform-maximal)
(unicode-name ,@prot-vertico-multiform-maximal)
;; Minimal
(file ,@prot-vertico-multiform-minimal
(vertico-preselect . prompt)
(vertico-sort-function . prot-vertico-sort-directories-first))
(vertico-sort-function . vertico-sort-directories-first))
(t ,@prot-vertico-multiform-minimal)))
(vertico-multiform-mode 1))
(vertico-multiform-mode 1)
(prot-emacs-keybind vertico-map
"<left>" #'backward-char
"<right>" #'forward-char
"<next>" #'vertico-next-group
"<prior>" #'vertico-previous-group
"TAB" #'prot-vertico-private-complete
"DEL" #'vertico-directory-delete-char
"M-DEL" #'vertico-directory-delete-word
"M-," #'vertico-quick-insert
"M-." #'vertico-quick-exit)
(prot-emacs-keybind vertico-multiform-map
"RET" #'prot-vertico-private-exit
"<return>" #'prot-vertico-private-exit
"C-n" #'prot-vertico-private-next
"<down>" #'prot-vertico-private-next
"C-p" #'prot-vertico-private-previous
"<up>" #'prot-vertico-private-previous
"C-l" #'vertico-multiform-vertical))
(provide 'prot-emacs-vertico)

View file

@ -1,188 +1,166 @@
;;;; `browse-url'
(use-package browse-url
:ensure nil
:defer t
:config
(prot-emacs-configure
(setq browse-url-browser-function 'eww-browse-url)
(setq browse-url-secondary-browser-function 'browse-url-default-browser))
;;;; `goto-addr'
(use-package goto-addr
:ensure nil
:defer t
:config
(prot-emacs-configure
(defun prot/goto-address-mode ()
"Enable `goto-address-mode' only in `text-mode' and no derivatives."
(when (eq major-mode 'text-mode)
(goto-address-mode 1)))
(add-hook 'text-mode-hook #'prot/goto-address-mode)
(add-hook 'prog-mode-hook #'goto-address-prog-mode)
(setq goto-address-url-face 'link)
(setq goto-address-url-mouse-face 'highlight)
(setq goto-address-mail-face nil)
(setq goto-address-mail-mouse-face 'highlight))
;;;; `shr' (Simple HTML Renderer)
(use-package shr
:ensure nil
:defer t
:config
(prot-emacs-configure
(setq shr-use-colors nil) ; t is bad for accessibility
(setq shr-use-fonts nil) ; t is not for me
(setq shr-use-fonts nil) ; t is superfluous, given `variable-pitch-mode'
(setq shr-max-image-proportion 0.6)
(setq shr-image-animate nil) ; No GIFs, thank you!
(setq shr-width fill-column) ; check `prot-eww-readable'
(setq shr-width fill-column)
(setq shr-max-width fill-column)
(setq shr-discard-aria-hidden t)
(setq shr-fill-text nil) ; Emacs 31
(setq shr-cookie-policy nil))
;;;; `url-cookie'
(use-package url-cookie
:ensure nil
:defer t
:config
(prot-emacs-configure
(setq url-cookie-untrusted-urls '(".*")))
;;;; `eww' (Emacs Web Wowser)
(use-package eww
:ensure nil
:commands (eww)
:bind
( :map eww-link-keymap
("v" . nil) ; stop overriding `eww-view-source'
:map eww-mode-map
("L" . eww-list-bookmarks)
:map dired-mode-map
("E" . eww-open-file) ; to render local HTML files
:map eww-buffers-mode-map
("d" . eww-bookmark-kill) ; it actually deletes
:map eww-bookmark-mode-map
("d" . eww-bookmark-kill)) ; same
:config
(setq eww-restore-desktop t)
(setq eww-desktop-remove-duplicates t)
(setq eww-header-line-format nil)
(setq eww-search-prefix "https://duckduckgo.com/html/?q=")
(setq eww-download-directory (expand-file-name "~/Documents/eww-downloads"))
(setq eww-suggest-uris
'(eww-links-at-point
thing-at-point-url-at-point))
(setq eww-bookmarks-directory (locate-user-emacs-file "eww-bookmarks/"))
(setq eww-history-limit 150)
(setq eww-use-external-browser-for-content-type
"\\`\\(video/\\|audio\\)") ; On GNU/Linux check your mimeapps.list
(setq eww-browse-url-new-window-is-tab nil)
(setq eww-form-checkbox-selected-symbol "[X]")
(setq eww-form-checkbox-symbol "[ ]")
;; NOTE `eww-retrieve-command' is for Emacs28. I tried the following
;; two values. The first would not render properly some plain text
;; pages, such as by messing up the spacing between paragraphs. The
;; second is more reliable but feels slower. So I just use the
;; default (nil), though I find wget to be a bit faster. In that case
;; one could live with the occasional errors by using `eww-download'
;; on the offending page, but I prefer consistency.
;;
;; '("wget" "--quiet" "--output-document=-")
;; '("chromium" "--headless" "--dump-dom")
(setq eww-retrieve-command nil))
(prot-emacs-configure
(with-eval-after-load 'eww
(autoload #'prot-simple-buffers-major-mode "prot-simple")
(prot-emacs-keybind eww-mode-map
"S" nil ; unmap `eww-list-buffers'
"b" #'prot-simple-buffers-major-mode ; a general version to show buffer of current mode
"m" #'bookmark-set)
(define-key eww-link-keymap (kbd "v") nil) ; stop overriding `eww-view-source'
(with-eval-after-load 'dired
(define-key dired-mode-map (kbd "E") #'eww-open-file)) ; to render local HTML files
(setq eww-auto-rename-buffer 'title)
(setq eww-header-line-format nil)
(setq eww-bookmarks-directory (locate-user-emacs-file "eww-bookmarks/"))
(setq eww-history-limit 150)
(setq eww-use-external-browser-for-content-type
"\\`\\(video/\\|audio\\)") ; On GNU/Linux check your mimeapps.list
(setq eww-form-checkbox-selected-symbol "[X]")
(setq eww-form-checkbox-symbol "[ ]")
;; NOTE `eww-retrieve-command' is for Emacs28. I tried the following
;; two values. The first would not render properly some plain text
;; pages, such as by messing up the spacing between paragraphs. The
;; second is more reliable but feels slower. So I just use the
;; default (nil), though I find wget to be a bit faster. In that case
;; one could live with the occasional errors by using `eww-download'
;; on the offending page, but I prefer consistency.
;;
;; '("wget" "--quiet" "--output-document=-")
;; '("chromium" "--headless" "--dump-dom")
(setq eww-retrieve-command nil)
;; NOTE 2025-02-15: Emacs has a robust framework for writing
;; bookmarks, which `eww' supports. Though `eww' also defines its
;; own parallel bookmark data, which I do not want to use. So here
;; I disable all the relevant commands.
(dolist (command '( eww-list-bookmarks eww-add-bookmark eww-bookmark-mode
eww-list-buffers eww-toggle-fonts eww-toggle-colors
eww-switch-to-buffer))
(put command 'disabled t))))
;;;; `prot-eww' extras
(use-package prot-eww
:ensure nil
:after eww
:config
(setq prot-eww-save-history-file
(locate-user-emacs-file "prot-eww-visited-history"))
(setq prot-eww-save-visited-history t)
(setq prot-eww-bookmark-link nil)
(add-hook 'prot-eww-history-mode-hook #'hl-line-mode)
(define-prefix-command 'prot-eww-map)
(define-key global-map (kbd "C-c w") 'prot-eww-map)
(prot-emacs-keybind prot-eww-map
"b" #'prot-eww-visit-bookmark
"e" #'prot-eww-browse-dwim
"s" #'prot-eww-search-engine)
(prot-emacs-keybind eww-mode-map
"B" #'prot-eww-bookmark-page
"D" #'prot-eww-download-html
"F" #'prot-eww-find-feed
"H" #'prot-eww-list-history
"b" #'prot-eww-visit-bookmark
"e" #'prot-eww-browse-dwim
"o" #'prot-eww-open-in-other-window
"E" #'prot-eww-visit-url-on-page
"J" #'prot-eww-jump-to-url-on-page
"R" #'prot-eww-readable
"Q" #'prot-eww-quit))
(prot-emacs-configure
(with-eval-after-load 'eww
(require 'prot-eww)
(prot-emacs-keybind eww-mode-map
"F" #'prot-eww-find-feed
"o" #'prot-eww-open-in-other-window
"j" #'prot-eww-jump-to-url-on-page
"J" #'prot-eww-visit-url-on-page)))
;;; Elfeed feed/RSS reader
(use-package elfeed
:ensure t
:bind
("C-c e" . elfeed)
:config
(setq elfeed-use-curl nil)
(setq elfeed-curl-max-connections 10)
(setq elfeed-db-directory (concat user-emacs-directory "elfeed/"))
(setq elfeed-enclosure-default-dir "~/Downloads/")
(setq elfeed-search-filter "@2-weeks-ago +unread")
(setq elfeed-sort-order 'descending)
(setq elfeed-search-clipboard-type 'CLIPBOARD)
(setq elfeed-search-title-max-width 100)
(setq elfeed-search-title-min-width 30)
(setq elfeed-search-trailing-width 25)
(setq elfeed-show-truncate-long-urls t)
(setq elfeed-show-unique-buffers t)
(setq elfeed-search-date-format '("%F %R" 16 :left))
(prot-emacs-configure
(prot-emacs-install elfeed)
;; Make sure to also check the section on shr and eww for how I handle
;; `shr-width' there.
(add-hook 'elfeed-show-mode-hook
(lambda () (setq-local shr-width (current-fill-column))))
(define-key global-map (kbd "C-c e") #'elfeed)
(prot-emacs-keybind elfeed-search-mode-map
"w" #'elfeed-search-yank
"g" #'elfeed-update
"G" #'elfeed-search-update--force)
(with-eval-after-load 'elfeed
(define-key elfeed-show-mode-map (kbd "w") #'elfeed-show-yank))
(add-hook 'elfeed-show-mode-hook #'visual-line-mode)
(use-package prot-elfeed
:ensure nil
:after elfeed
:bind
( :map elfeed-search-mode-map
("s" . prot-elfeed-search-tag-filter)
("+" . prot-elfeed-toggle-tag)
:map elfeed-show-mode-map
("+" . prot-elfeed-toggle-tag))
:hook
(elfeed-search-mode . prot-elfeed-load-feeds)
:config
(setq prot-elfeed-tag-faces t)
(prot-elfeed-fontify-tags))
(prot-emacs-keybind elfeed-search-mode-map
"w" #'elfeed-search-yank
"g" #'elfeed-update
"G" #'revert-buffer)
(define-key elfeed-show-mode-map (kbd "w") #'elfeed-show-yank)
(setq elfeed-last-update-relative 0) ; absolute time in the header-line
(setq elfeed-search-separator-date-format nil) ; no date sections in the search buffer
(setq elfeed-use-curl nil)
(setq elfeed-curl-max-connections 10)
(setq elfeed-db-directory (expand-file-name "elfeed/" user-emacs-directory))
(setq elfeed-enclosure-default-dir (expand-file-name "~/Downloads/"))
(setq-default elfeed-search-filter "@2-weeks-ago +unread") ; NOTE 2026-05-28: was changed to buffer-local
(setq elfeed-sort-order 'descending)
(setq elfeed-search-clipboard-type 'CLIPBOARD)
(setq elfeed-search-title-max-width 100)
(setq elfeed-search-title-min-width 30)
(setq elfeed-search-trailing-width 25)
(setq elfeed-show-truncate-long-urls t)
(setq elfeed-show-unique-buffers t)
(setq elfeed-search-date-format '("%F %R" 20 :left))
;; NOTE 2025-10-28: Instead of showing my `elfeed-feeds' here, I put
;; it in its own Emacs Lisp file. The `load' function will evaluate
;; that code. Internally, the file has just the following:
;;
;; (setq elfeed-feeds FEEDS)
(unless (bound-and-true-p elfeed-feeds)
(load (expand-file-name "~/feeds.el.gpg") :no-error :no-message))
;; Make entries tagged as "personal" use the `bold-italic' face.
;; This way I can quickly notice my own entries in the listing.
(add-to-list 'elfeed-search-face-alist '(personal bold-italic))
;; NOTE 2026-05-29: Make `elfeed-search-yank' mark the entry as
;; read. If I am copying the URL, then it means I already plan to
;; do something with this entry outside of Elfeed.
(define-advice elfeed-search-yank (:around (&rest args) prot)
(save-excursion
(apply args))
(call-interactively 'elfeed-search-untag-all-unread))))
;;; Rcirc (IRC client)
(use-package rcirc
:ensure nil
:bind ("C-c i" . irc)
:config
(setq rcirc-server-alist
`(("irc.libera.chat"
:channels ("#emacs" "#rcirc")
:port 6697
:encryption tls
:password ,(prot-common-auth-get-field "libera" :secret))))
(prot-emacs-configure
(with-eval-after-load 'rcirc
(setq rcirc-server-alist
`(("irc.libera.chat"
:channels ("#emacs")
:port 6697
:encryption tls
:password ,(prot-common-auth-get-field "libera" :secret))))
(setq rcirc-prompt "%t> ") ; Read the docs or use (customize-set-variable 'rcirc-prompt "%t> ")
(setq rcirc-prompt "%t> ") ; Read the docs or use (customize-set-variable 'rcirc-prompt "%t> ")
(setq rcirc-default-nick "protesilaos"
rcirc-default-user-name rcirc-default-nick
rcirc-default-full-name "Protesilaos Stavrou")
(setq rcirc-default-nick "protesilaos"
rcirc-default-user-name "protesilaos"
rcirc-default-full-name "Protesilaos")
;; ;; NOTE 2021-11-28: demo from the days of EmacsConf 2021. I don't
;; ;; actually need this.
;; (setq rcirc-bright-nicks '("bandali" "sachac" "zaeph"))
;; ;; NOTE 2021-11-28: demo from the days of EmacsConf 2021. I don't
;; ;; actually need this.
;; (setq rcirc-bright-nicks '("bandali" "sachac" "zaeph"))
;; NOTE 2021-11-28: Is there a canonical way to disable this?
(setq rcirc-timeout-seconds most-positive-fixnum)
;; NOTE 2021-11-28: Is there a canonical way to disable this?
(setq rcirc-timeout-seconds most-positive-fixnum)
(rcirc-track-minor-mode 1))
(rcirc-track-minor-mode 1)))
(provide 'prot-emacs-web)

View file

@ -1,13 +1,12 @@
(use-package which-key
:ensure t
:hook (after-init . which-key-mode)
:config
(prot-emacs-configure
(setq which-key-separator " ")
(setq which-key-prefix-prefix "... ")
(setq which-key-max-display-columns 3)
(setq which-key-idle-delay 1.5)
(setq which-key-idle-secondary-delay 0.25)
(setq which-key-add-column-padding 1)
(setq which-key-max-description-length 40))
(setq which-key-max-description-length 40)
(which-key-mode 1)) ; built into Emacs 30
(provide 'prot-emacs-which-key)

View file

@ -1,197 +1,213 @@
(prot-emacs-configure
(setq tab-bar-new-button-show nil)
(setq tab-bar-close-button-show nil)
(setq tab-bar-show 1)
(advice-add #'window--delete :after #'prot-common-clear-minibuffer-message))
;;; General window and buffer configurations
(use-package uniquify
:ensure nil
:config
(prot-emacs-configure
;;;; `uniquify' (unique names for buffers)
(setq uniquify-buffer-name-style 'forward)
(setq uniquify-strip-common-suffix t)
(setq uniquify-after-kill-buffer-p t))
;;;; Line highlight
(use-package hl-line
:ensure nil
:commands (hl-line-mode)
:config
(prot-emacs-configure
(setq hl-line-sticky-flag nil)
(setq hl-line-overlay-priority -50)) ; emacs28
;;;; Negative space highlight
(use-package whitespace
:ensure nil
:bind
(("<f6>" . whitespace-mode)
("C-c z" . delete-trailing-whitespace))
:config
;; NOTE 2023-08-14: This is experimental. I am not sure I like it.
(prot-emacs-configure
(setq whitespace-style
'(face
tabs
spaces
newline
tab-mark
space-mark
newline-mark
trailing
missing-newline-at-eof
space-after-tab::tab
space-after-tab::space
space-before-tab::tab
space-before-tab::space)))
space-before-tab::space))
(prot-emacs-keybind global-map
"<f6>" #'whitespace-mode
"C-c z" #'delete-trailing-whitespace))
;;; Line numbers on the side of the window
(use-package display-line-numbers
:ensure nil
:bind
("<f7>" . display-line-numbers-mode)
:config
(setq-default display-line-numbers-type t)
(prot-emacs-configure
(setq display-line-numbers-type t)
;; Those two variables were introduced in Emacs 27.1
(setq display-line-numbers-major-tick 0)
(setq display-line-numbers-minor-tick 0)
;; Use absolute numbers in narrowed buffers
(setq-default display-line-numbers-widen t))
;;;; `window', `display-buffer-alist', and related
(use-package prot-window
:ensure nil
:demand t
:config
;; NOTE 2023-03-17: Remember that I am using development versions of
;; Emacs. Some of my `display-buffer-alist' contents are for Emacs
;; 29+.
(setq display-buffer-alist
`(;; no window
("\\`\\*Async Shell Command\\*\\'"
(display-buffer-no-window))
("\\`\\*\\(Warnings\\|Compile-Log\\|Org Links\\)\\*\\'"
(display-buffer-no-window)
(allow-no-window . t))
;; bottom side window
("\\*Org \\(Select\\|Note\\)\\*" ; the `org-capture' key selection and `org-add-log-note'
(display-buffer-in-side-window)
(dedicated . t)
(side . bottom)
(slot . 0)
(window-parameters . ((mode-line-format . none))))
;; bottom buffer (NOT side window)
((or . ((derived-mode . flymake-diagnostics-buffer-mode)
(derived-mode . flymake-project-diagnostics-mode)
(derived-mode . messages-buffer-mode)
(derived-mode . backtrace-mode)))
(display-buffer-reuse-mode-window display-buffer-at-bottom)
(window-height . 0.3)
(dedicated . t)
(preserve-size . (t . t)))
("\\*Embark Actions\\*"
(display-buffer-reuse-mode-window display-buffer-below-selected)
(window-height . fit-window-to-buffer)
(window-parameters . ((no-other-window . t)
(mode-line-format . none))))
("\\*\\(Output\\|Register Preview\\).*"
(display-buffer-reuse-mode-window display-buffer-at-bottom))
;; below current window
("\\(\\*Capture\\*\\|CAPTURE-.*\\)"
(display-buffer-reuse-mode-window display-buffer-below-selected))
("\\*\\vc-\\(incoming\\|outgoing\\|git : \\).*"
(display-buffer-reuse-mode-window display-buffer-below-selected)
(window-height . 0.1)
(dedicated . t)
(preserve-size . (t . t)))
((derived-mode . reb-mode) ; M-x re-builder
(display-buffer-reuse-mode-window display-buffer-below-selected)
(window-height . 4) ; note this is literal lines, not relative
(dedicated . t)
(preserve-size . (t . t)))
((or . ((derived-mode . occur-mode)
(derived-mode . grep-mode)
(derived-mode . Buffer-menu-mode)
(derived-mode . log-view-mode)
(derived-mode . help-mode) ; See the hooks for `visual-line-mode'
"\\*\\(|Buffer List\\|Occur\\|vc-change-log\\|eldoc.*\\).*"
prot-window-shell-or-term-p
;; ,world-clock-buffer-name
))
(prot-window-display-buffer-below-or-pop)
(body-function . prot-window-select-fit-size))
("\\*\\(Calendar\\|Bookmark Annotation\\|ert\\).*"
(display-buffer-reuse-mode-window display-buffer-below-selected)
(dedicated . t)
(window-height . fit-window-to-buffer))
;; NOTE 2022-09-10: The following is for `ispell-word', though
;; it only works because I override `ispell-display-buffer'
;; with `prot-spell-ispell-display-buffer' and change the
;; value of `ispell-choices-buffer'.
("\\*ispell-top-choices\\*.*"
(display-buffer-reuse-mode-window display-buffer-below-selected)
(window-height . fit-window-to-buffer))
;; same window
(prot-emacs-configure
(setq prot-streaming-mode-modes '(text-mode prog-mode dired-mode comint-mode))
(prot-emacs-autoload prot-streaming-mode "prot-streaming")
(define-key global-map (kbd "<f7>") #'prot-streaming-mode))
;; NOTE 2023-02-17: `man' does not fully obey the
;; `display-buffer-alist'. It works for new frames and for
;; `display-buffer-below-selected', but otherwise is
;; unpredictable. See `Man-notify-method'.
((or . ((derived-mode . Man-mode)
(derived-mode . woman-mode)
"\\*\\(Man\\|woman\\).*"))
(display-buffer-same-window)))))
;;;; `window', `display-buffer-alist', and related
(prot-emacs-configure
(require 'prot-window)
;; NOTE 2023-03-17: Remember that I am using development versions of
;; Emacs. Some of my `display-buffer-alist' contents are for Emacs
;; 29+.
(setq display-buffer-alist
`(;; no window
("\\`\\*Async Shell Command\\*\\'"
(display-buffer-no-window))
("\\`\\*\\(Warnings\\|Compile-Log\\|Org Links\\)\\*\\'"
(display-buffer-no-window)
(allow-no-window . t))
;; bottom side window
("\\*\\(Org \\(Select\\|Note\\)\\|Agenda Commands\\)\\*" ; the `org-capture' key selection and `org-add-log-note'
(display-buffer-in-side-window)
(dedicated . t)
(side . bottom)
(slot . 0)
(window-parameters . ((mode-line-format . none))))
;; bottom buffer (NOT side window)
((or . ((derived-mode . flymake-diagnostics-buffer-mode)
(derived-mode . flymake-project-diagnostics-mode)
(derived-mode . messages-buffer-mode)
(derived-mode . backtrace-mode)))
(display-buffer-reuse-mode-window display-buffer-at-bottom)
(mode . ( flymake-diagnostics-buffer-mode flymake-project-diagnostics-mode
messages-buffer-mode backtrace-mode))
(inhibit-switch-frame . t)
(window-height . 0.3)
(dedicated . t)
(preserve-size . (t . t))
(body-function . select-window))
("\\*Embark Actions\\*"
;; NOTE 2026-07-14: I am including `display-buffer-at-bottom' here because Embark can be
;; called from the minibuffer where `display-buffer-below-selected' does not work as intended.
(display-buffer-reuse-mode-window display-buffer-below-selected display-buffer-at-bottom)
(window-height . fit-window-to-buffer)
(window-parameters . ((no-other-window . t)
(mode-line-format . none))))
("\\*\\(Output\\|Register Preview\\).*"
(display-buffer-reuse-mode-window display-buffer-at-bottom)
(inhibit-switch-frame . t))
;; below current window
("\\*prot-elisp-macroexpand\\*"
(display-buffer-below-selected)
(window-height . 0.3)
(dedicated . t)
(preserve-size . (t . t))
(body-function . select-window))
((derived-mode . reb-mode) ; M-x re-builder
(display-buffer-reuse-mode-window display-buffer-below-selected)
(inhibit-switch-frame . t)
(window-height . 4) ; note this is literal lines, not relative
(dedicated . t)
(preserve-size . (t . t)))
((or . ((derived-mode . occur-mode)
(derived-mode . grep-mode)
(derived-mode . Buffer-menu-mode)
(derived-mode . log-view-mode)
(derived-mode . help-mode) ; See the hooks for `visual-line-mode'
"\\*\\(|Buffer List\\|Occur\\|vc-change-log\\|eldoc.*\\).*"))
(prot-window-display-buffer-below-or-pop)
(body-function . prot-window-select-fit-size))
(prot-window-shell-or-term-p
(display-buffer-reuse-mode-window display-buffer-at-bottom)
(mode . (shell-mode eshell-mode comint-mode))
(inhibit-switch-frame . t)
(body-function . prot-window-select-fit-size))
((derived-mode . vc-compilation-mode)
(display-buffer-reuse-mode-window display-buffer-below-selected)
(inhibit-switch-frame . t)
(dedicated . t)
(window-height . 0.1))
((or . ((derived-mode . calendar-mode)
(derived-mode . ert-results-mode)))
(display-buffer-reuse-mode-window display-buffer-below-selected)
(mode . (calendar-mode ert-results-mode))
(inhibit-switch-frame . t)
(dedicated . t)
(window-height . fit-window-to-buffer))
;; NOTE 2022-09-10: The following is for `ispell-word', though
;; it only works because I override `ispell-display-buffer'
;; with `prot-spell-ispell-display-buffer' and change the
;; value of `ispell-choices-buffer'.
("\\*ispell-top-choices\\*.*"
(display-buffer-below-selected)
(window-height . fit-window-to-buffer))
;; current window
((or . (;; NOTE 2023-02-17: `man' does not fully obey the
;; `display-buffer-alist'. It works for new frames
;; and for `display-buffer-below-selected', but
;; otherwise is unpredictable. See `Man-notify-method'.
(derived-mode . Man-mode)
(derived-mode . woman-mode)
"\\*\\(Man\\|woman\\).*"))
(display-buffer-reuse-mode-window display-buffer-same-window))
((or . ((derived-mode . magit-diff-mode)
(derived-mode . magit-log-mode)))
(display-buffer-reuse-mode-window prot-window-display-new display-buffer-pop-up-window)
(inhibit-switch-frame . t))
;; other `tab-bar-mode' tab
((or . ((derived-mode . magit-status-mode)
(derived-mode . vc-dir-mode)
"\\*.*vc-dir.*\\*"))
(display-buffer-reuse-mode-window display-buffer-in-tab)
(mode . (magit-status-mode vc-dir-mode))
(tab-name . (lambda (buffer _alist) (buffer-name buffer)))
(reusable-frames . :just-the-selected-frame)
(inhibit-switch-frame . t)))))
(use-package prot-window
:ensure nil
:demand t
:config
(prot-emacs-configure
(setq split-window-preferred-direction 'horizontal) ; Emacs 31
(setq window-combination-resize t)
(setq even-window-sizes 'height-only)
(setq window-sides-vertical nil)
(setq switch-to-buffer-in-dedicated-window 'pop)
(setq split-height-threshold 80)
(setq split-height-threshold 85)
(setq split-width-threshold 125)
(setq window-min-height 3)
(setq window-min-width 30))
(use-package prot-window
:ensure nil
:demand t
:hook
((epa-info-mode help-mode custom-mode) . visual-line-mode))
(prot-emacs-configure
(prot-emacs-hook (epa-info-mode-hook help-mode-hook custom-mode-hook) visual-line-mode))
(use-package prot-window
:ensure nil
:demand t
:hook
((world-clock-mode calendar-mode) . prot-common-truncate-lines-silently))
(prot-emacs-configure
(prot-emacs-hook (world-clock-mode-hook calendar-mode-hook) prot-common-truncate-lines-silently))
(use-package prot-window
:ensure nil
:demand t
:bind
( :map global-map
(prot-emacs-configure
(prot-emacs-keybind global-map
;; NOTE 2022-09-17: Also see `prot-simple-swap-window-buffers'.
("C-x <down>" . next-buffer)
("C-x <up>" . previous-buffer)
("C-x C-n" . next-buffer) ; override `set-goal-column'
("C-x C-p" . previous-buffer) ; override `mark-page'
("C-x !" . delete-other-windows-vertically)
("C-x _" . balance-windows) ; underscore
("C-x -" . fit-window-to-buffer) ; hyphen
("C-x +" . balance-windows-area)
("C-x }" . enlarge-window)
("C-x {" . shrink-window)
("C-x >" . enlarge-window-horizontally) ; override `scroll-right'
("C-x <" . shrink-window-horizontally) ; override `scroll-left'
:map resize-window-repeat-map
(">" . enlarge-window-horizontally)
("<" . shrink-window-horizontally)))
"C-x <down>" #'next-buffer
"C-x <up>" #'previous-buffer
"C-x C-n" #'next-buffer ; override `set-goal-column'
"C-x C-p" #'previous-buffer ; override `mark-page'
"C-x !" #'delete-other-windows-vertically
"C-x _" #'balance-windows ; underscore
"C-x -" #'fit-window-to-buffer ; hyphen
"C-x +" #'balance-windows-area
"C-x }" #'enlarge-window
"C-x {" #'shrink-window
"C-x >" #'enlarge-window-horizontally ; override `scroll-right'
"C-x <" #'shrink-window-horizontally) ; override `scroll-left'
(prot-emacs-keybind resize-window-repeat-map
">" #'enlarge-window-horizontally
"<" #'shrink-window-horizontally))
;;; Frame-isolated buffers
;; Another package of mine. Read the manual:
;; <https://protesilaos.com/emacs/beframe>.
(use-package beframe
:ensure t
:hook (after-init . beframe-mode)
:config
(prot-emacs-configure
(prot-emacs-install beframe)
(require 'beframe)
(setq beframe-functions-in-frames '(project-prompt-project-dir))
;; I use this instead of :bind because I am binding a keymap and the
;; way `use-package' does it is by wrapping a lambda around it that
;; then breaks `describe-key' for those keys.
(prot-emacs-keybind global-map
;; Override the `set-fill-column' that I have no use for.
"C-x f" #'other-frame-prefix
@ -204,47 +220,102 @@
"C-x C-b" #'beframe-buffer-menu
;; Not specific to Beframe, but since it renames frames (by means
;; of `beframe-mode') it is appropriate to have this here:
"C-x B" #'select-frame-by-name))
"C-x B" #'select-frame-by-name)
(beframe-mode 1)
(with-eval-after-load 'consult
(defface beframe-buffer
'((t :inherit font-lock-string-face))
"Face for `consult' framed buffers.")
(defun prot/beframe-buffer-names-sorted (&optional frame)
"Return the list of buffers from `beframe-buffer-names' sorted by visibility.
With optional argument FRAME, return the list of buffers of FRAME."
(beframe-buffer-names frame :sort #'beframe-buffer-sort-visibility))
(defvar beframe-consult-source
`( :name "Frame-specific buffers (current frame)"
:narrow ?F
:category buffer
:face beframe-buffer
:history beframe-history
:items ,#'prot/beframe-buffer-names-sorted
:action ,#'switch-to-buffer
:state ,#'consult--buffer-state))
(add-to-list 'consult-buffer-sources 'beframe-consult-source)))
;;; Frame history (undelete-frame-mode)
(use-package frame
:ensure nil
:bind ("C-x u" . undelete-frame) ; I use only C-/ for `undo'
:hook (after-init . undelete-frame-mode))
(prot-emacs-configure
(define-key global-map (kbd "C-x u") #'undelete-frame) ; I use only C-/ for `undo'
(undelete-frame-mode 1))
;;; Window history (winner-mode)
(use-package winner
:ensure nil
:hook (after-init . winner-mode)
:bind
(("C-x <right>" . winner-redo)
("C-x <left>" . winner-undo)))
(prot-emacs-configure
(prot-emacs-keybind global-map
"C-x <right>" #'winner-redo
"C-x <left>" #'winner-undo)
(winner-mode 1))
;;; Directional window motions (windmove)
(use-package windmove
:ensure nil
:bind
(prot-emacs-configure
;; Those override some commands that are already available with
;; C-M-u, C-M-f, C-M-b.
(("C-M-<up>" . windmove-up)
("C-M-<right>" . windmove-right)
("C-M-<down>" . windmove-down)
("C-M-<left>" . windmove-left)
("C-M-S-<up>" . windmove-swap-states-up)
("C-M-S-<right>" . windmove-swap-states-right) ; conflicts with `org-increase-number-at-point'
("C-M-S-<down>" . windmove-swap-states-down)
("C-M-S-<left>" . windmove-swap-states-left))
:config
(prot-emacs-keybind global-map
"C-M-<up>" #'windmove-up
"C-M-<right>" #'windmove-right
"C-M-<down>" #'windmove-down
"C-M-<left>" #'windmove-left
"C-M-S-<up>" #'windmove-swap-states-up
"C-M-S-<right>" #'windmove-swap-states-right ; conflicts with `org-increase-number-at-point'
"C-M-S-<down>" #'windmove-swap-states-down
"C-M-S-<left>" #'windmove-swap-states-left)
(setq windmove-create-window nil)) ; Emacs 27.1
;;; Header line context of symbol/heading (breadcrumb.el)
(use-package breadcrumb
:ensure t
:hook (after-init . breadcrumb-mode)
:config
(setq breadcrumb-project-max-length 0.5)
(setq breadcrumb-project-crumb-separator "/")
(setq breadcrumb-imenu-max-length 1.0)
(setq breadcrumb-imenu-crumb-separator " > "))
;;; Buffer to PDF
(prot-emacs-configure
;; The use the command `buffer-to-pdf' or `buffer-to-pdf-black-on-white'.
(prot-emacs-install-vc
buffer-to-pdf
"https://github.com/protesilaos/buffer-to-pdf.git"))
(prot-emacs-configure
(setq speedbar-use-images nil) ; t here refers to the `cdr' of each `speedbar-expand-image-button-alist'
(setq speedbar-smart-directory-expand-flag nil)
(setq speedbar-prefer-window t ; both of these are for Emacs 31
speedbar-window-max-width 60)
(setq speedbar-indentation-width 4)
(setq speedbar-show-unknown-files t)
;; NOTE 2026-08-05 17:08 +0300: Waiting for https://debbugs.gnu.org/cgi/bugreport.cgi?bug=81556 which I just submitted.
(setq speedbar-directory-unshown-regexp nil
speedbar-file-unshown-regexp nil)
(prot-emacs-hook speedbar-visiting-tag-hook (speedbar-recenter speedbar-highlight-one-tag-line))
(defun prot/speedbar-imenu-flatten (elements)
"Flatten the Imenu ELEMENTS."
(let* ((elements (imenu--flatten-index-alist elements t))
(elements-no-prefix (mapcar
(lambda (element)
(when-let* ((string (car element))
(position (cdr element))
(_ (or (natnump position) (markerp position))))
(cond
((string-match-p ":.*:" string)
(cons
(string-join (cdr (split-string string "[:]")))
position))
((string-match-p ":" string)
(cons
(cadr (split-string string "[:]"))
position))
(t
element))))
elements)))
(delq nil elements-no-prefix)))
(setq speedbar-tag-hierarchy-method '(prot/speedbar-imenu-flatten)))
(provide 'prot-emacs-window)

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,8 @@
;;; prot-abbrev.el --- Functions for use with abbrev-mode -*- lexical-binding: t -*-
;; Copyright (C) 2024 Protesilaos Stavrou
;; Copyright (C) 2025-2026 Protesilaos
;; Author: Protesilaos Stavrou <info@protesilaos.com>
;; Author: Protesilaos <info@protesilaos.com>
;; URL: https://protesilaos.com/emacs/dotemacs
;; Version: 0.1.0
;; Package-Requires: ((emacs "30.1"))
@ -25,11 +25,6 @@
;;; Commentary:
;;
;; Functions for use with `abbrev-mode'.
;;
;; Remember that every piece of Elisp that I write is for my own
;; educational and recreational purposes. I am not a programmer and I
;; do not recommend that you copy any of this if you are not certain of
;; what it does.
;;; Code:
@ -75,5 +70,39 @@ Use completion among previous entries, retrieving their data from
"Insert message to update NAME.html page, by prompting for NAME."
(insert (format "Update %s.html" (prot-abbrev-update-html-prompt))))
(defvar prot-abbrev-org-macro-key-history nil
"Minibuffer history for `prot-abbrev-org-macro-key-prompt'.")
(defun prot-abbrev-org-macro-key-prompt ()
"Minibuffer prompt for `prot-abbrev-org-macro-key'.
Use completion among previous entries, retrieving their data from
`prot-abbrev-org-macro-key-history'."
(completing-read
"Key binding: "
prot-abbrev-org-macro-key-history
nil nil nil 'prot-abbrev-org-macro-key-history))
(defvar prot-abbrev-org-macro-key-symbol-history nil
"Minibuffer history for `prot-abbrev-org-macro-key-symbol-prompt'.")
(defun prot-abbrev-org-macro-key-symbol-prompt ()
"Minibuffer prompt for `prot-abbrev-org-macro-key'.
Use completion among previous entries, retrieving their data from
`prot-abbrev-org-macro-key-symbol-history'."
(completing-read
"Command name: "
prot-abbrev-org-macro-key-symbol-history
nil nil nil 'prot-abbrev-org-macro-key-symbol-history))
(defun prot-abbrev-org-macro-key-command ()
"Insert {{{kbd(KEY)}}} (~SYMBOL~) by prompting for KEY and SYMBOL."
(insert (format "{{{kbd(%s)}}} (~%s~)"
(prot-abbrev-org-macro-key-prompt)
(prot-abbrev-org-macro-key-symbol-prompt))))
(defun prot-abbrev-org-macro-key ()
"Insert {{{kbd(KEY)}}} by prompting for KEY."
(insert (format "{{{kbd(%s)}}}" (prot-abbrev-org-macro-key-prompt))))
(provide 'prot-abbrev)
;;; prot-abbrev.el ends here

View file

@ -1,8 +1,8 @@
;;; prot-coach.el --- Code for my personal coaching sessions -*- lexical-binding: t -*-
;; Copyright (C) 2023 Protesilaos Stavrou
;; Copyright (C) 2023-2026 Protesilaos
;; Author: Protesilaos Stavrou <info@protesilaos.com>
;; Author: Protesilaos <info@protesilaos.com>
;; URL: https://protesilaos.com/emacs/dotemacs
;; Version: 0.1.0
;; Package-Requires: ((emacs "30.1"))
@ -43,11 +43,11 @@
(defun prot-coach--name-prompt-default ()
"Return default value for `prot-coach-name-prompt'."
(when-let ((from (cond
((derived-mode-p 'message-mode)
(message-fetch-field "To"))
((derived-mode-p 'notmuch-show-mode)
(notmuch-show-get-header :From)))))
(when-let* ((from (cond
((derived-mode-p 'message-mode)
(message-fetch-field "To"))
((derived-mode-p 'notmuch-show-mode)
(notmuch-show-get-header :From)))))
(string-clean-whitespace (car (split-string from "<")))))
(defun prot-coach-name-prompt ()
@ -135,7 +135,7 @@ Omit entries with a CANCEL state."
(erase-buffer)
(goto-char (point-min))
(insert "# Type C-c C-c in the TBLFM line to produce the table\n\n")
(insert "* Cost counting without counting minutes\n")
(insert "* Cost without counting minutes\n")
(insert (concat
"\n"
"| Description | Started | Closed | Days | Hours | Cost (EUR) |" "\n"
@ -172,7 +172,7 @@ Omit entries with a CANCEL state."
(let (sessions)
(org-map-entries
(lambda ()
(when-let ((entry (prot-coach--get-deadline-and-close name)))
(when-let* ((entry (prot-coach--get-deadline-and-close name)))
(push entry sessions))))
(prot-coach--print-table-with-sessions name sessions))))

View file

@ -1,8 +1,8 @@
;;; prot-comment.el --- Extensions newcomment.el for my dotemacs -*- lexical-binding: t -*-
;; Copyright (C) 2021-2023 Protesilaos Stavrou
;; Copyright (C) 2021-2026 Protesilaos
;; Author: Protesilaos Stavrou <info@protesilaos.com>
;; Author: Protesilaos <info@protesilaos.com>
;; URL: https://protesilaos.com/emacs/dotemacs
;; Version: 0.1.0
;; Package-Requires: ((emacs "30.1"))
@ -26,11 +26,6 @@
;;
;; This covers my newcomment.el extras, for use in my Emacs setup:
;; https://protesilaos.com/emacs/dotemacs.
;;
;; Remember that every piece of Elisp that I write is for my own
;; educational and recreational purposes. I am not a programmer and I
;; do not recommend that you copy any of this if you are not certain of
;; what it does.
;;; Code:

View file

@ -1,8 +1,8 @@
;;; prot-common.el --- Common functions for my dotemacs -*- lexical-binding: t -*-
;; Copyright (C) 2020-2023 Protesilaos Stavrou
;; Copyright (C) 2020-2026 Protesilaos
;; Author: Protesilaos Stavrou <info@protesilaos.com>
;; Author: Protesilaos <info@protesilaos.com>
;; URL: https://protesilaos.com/emacs/dotemacs
;; Version: 0.1.0
;; Package-Requires: ((emacs "30.1"))
@ -25,14 +25,11 @@
;;; Commentary:
;;
;; Common functions for my Emacs: <https://protesilaos.com/emacs/dotemacs/>.
;;
;; Remember that every piece of Elisp that I write is for my own
;; educational and recreational purposes. I am not a programmer and I
;; do not recommend that you copy any of this if you are not certain of
;; what it does.
;;; Code:
;; TODO 2025-12-21: I need to review all this.
(eval-when-compile
(require 'subr-x)
(require 'cl-lib))
@ -41,6 +38,24 @@
"Auxiliary functions for my dotemacs."
:group 'editing)
(defvar prot-common-file-extensions
'((archive "tar" "rar" "zip" "xz")
(image "jpg" "png" "jpeg" "webp")
(audio "mp3" "flac" "ogg" "wav")
(video "mkv" "webm" "mp4")
(text "org" "txt" "md")
(document "pdf" "epub" "info" "texi")
(diff "diff" "patch")
(program "sh" "el" "c" "py" "js" "html" "xml")
(program-data "yaml" "yml" "toml" "conf" "json" "css"))
"Some common file extensions grouped by type.")
(defun prot-common--get-file-type-regexp (type)
"Return regular expression for file TYPE.
TYPE is a `car' among the elements of `prot-common-file-extensions'."
(when-let* ((extensions (alist-get type prot-common-file-extensions)))
(format "\\.%s\\'" (regexp-opt extensions))))
;;;###autoload
(defun prot-common-number-even-p (n)
"Test if N is an even number."
@ -127,6 +142,45 @@ floating points: 16.666666666666664 => 16.667."
(prot-common-seconds-to-hours seconds)
(prot-common-seconds-to-minutes seconds)))
(defvar prot-common-temperature-history nil
"Minibuffer history for temperature conversions.")
;;;###autoload
(defun prot-common-fahrenheit-to-celsius (degree)
"Convert Fahrenheit DEGREE to Celsius."
(interactive
(list
(read-number "Fahrenheit DEGREE: " nil 'prot-common-temperature-history)))
(let* ((converted-degrees (/ (- degree 32) 1.8))
(f-string (number-to-string degree))
(c-string (number-to-string converted-degrees)))
(message "%s degrees Fahrenheit is %s degrees Celsius"
(propertize f-string 'face 'error)
(propertize c-string 'face 'success))))
;;;###autoload
(defun prot-common-celsius-to-fahrenheit (degree)
"Convert Celsius DEGREE to Fahrenheit."
(interactive
(list
(read-number "Celsius DEGREE: " nil 'prot-common-temperature-history)))
(let* ((converted-degrees (+ (* degree 1.8) 32))
(c-string (number-to-string degree))
(f-string (number-to-string converted-degrees)))
(message "%s degrees Celsius is %s degrees Fahrenheit"
(propertize c-string 'face 'success)
(propertize f-string 'face 'error))))
;;;###autoload
(defun prot-common--rotate (elements)
"Rotate list ELEMENTS so that nth 0 is last, nth 1 is nth 0, and so on.
Do it without side effects, unlike `prot-common-rotate-list-of-symbol'."
(declare (side-effect-free t))
(setq elements (copy-sequence elements))
(let ((first (car elements))
(rest (cdr elements)))
(append rest (list first))))
;;;###autoload
(defun prot-common-rotate-list-of-symbol (symbol)
"Rotate list value of SYMBOL by moving its car to the end.
@ -167,7 +221,10 @@ will continue rotating accordingly."
;;;###autoload
(defun prot-common-truncate-lines-silently ()
"Toggle line truncation without printing messages."
(let ((inhibit-message t))
;; NOTE 2026-08-28: I used to only do `inhibit-message' via a `let',
;; but it was still printing messages.
(cl-letf (((symbol-function 'message) #'always)
(inhibit-message t))
(toggle-truncate-lines t)))
;; NOTE 2023-08-12: I tried the `clear-message-function', but it did
@ -227,7 +284,7 @@ If FRAME is non-nil, inspect the current frame."
;;;###autoload
(defun prot-common-completion-category ()
"Return completion category."
(when-let ((window (active-minibuffer-window)))
(when-let* ((window (active-minibuffer-window)))
(with-current-buffer (window-buffer window)
(completion-metadata-get
(completion-metadata (buffer-substring-no-properties
@ -339,7 +396,7 @@ Return the exit code and output in a list."
;;;###autoload
(defun prot-common-auth-get-field (host prop)
"Find PROP in `auth-sources' for HOST entry."
(when-let ((source (auth-source-search :host host)))
(when-let* ((source (auth-source-search :host host)))
(if (eq prop :secret)
(funcall (plist-get (car source) prop))
(plist-get (flatten-list source) prop))))

View file

@ -1,8 +1,8 @@
;;; prot-dired.el --- Extensions to dired.el for my dotemacs -*- lexical-binding: t -*-
;; Copyright (C) 2020-2023 Protesilaos Stavrou
;; Copyright (C) 2020-2026 Protesilaos
;; Author: Protesilaos Stavrou <info@protesilaos.com>
;; Author: Protesilaos <info@protesilaos.com>
;; URL: https://protesilaos.com/emacs/dotemacs
;; Version: 0.1.0
;; Package-Requires: ((emacs "30.1"))
@ -26,11 +26,6 @@
;;
;; This covers my dired.el extensions, for use in my Emacs setup:
;; https://protesilaos.com/emacs/dotemacs.
;;
;; Remember that every piece of Elisp that I write is for my own
;; educational and recreational purposes. I am not a programmer and I
;; do not recommend that you copy any of this if you are not certain of
;; what it does.
;;; Code:
@ -44,6 +39,90 @@
;;;; General commands
;;;###autoload
(defun prot-dired-count-marks ()
"Print message with the number of Dired marked files."
(interactive nil dired-mode)
(if-let* ((files (dired-get-marked-files))
(length (length files))
(_ (> length 1)))
(message "%d files are marked" length)
(user-error "No files found")))
;;;###autoload
(defun prot-dired-open-directory-externally ()
"Open the current directory in an external file manager."
(interactive nil dired-mode)
(if (derived-mode-p 'dired-mode)
(call-process "xdg-open" nil 0 nil (expand-file-name default-directory))
(user-error "Only do this in a Dired buffer")))
;;;; Flat Dired listing
(defvar prot-dired-regexp-history nil
"Minibuffer history of `prot-dired-regexp-prompt'.")
(defun prot-dired-regexp-prompt ()
(let ((default (car prot-dired-regexp-history)))
(read-regexp
(format-prompt "Files matching REGEXP" default)
default 'prot-dired-regexp-history)))
(defun prot-dired--get-files (regexp)
"Return files matching REGEXP, recursively from `default-directory'."
(directory-files-recursively default-directory regexp nil))
;;;###autoload
(defun prot-dired-search-flat-list (regexp)
"Return a Dired buffer for files matching REGEXP.
Perform the search recursively from the current directory."
(interactive (list (prot-dired-regexp-prompt)))
(if-let* ((files (prot-dired--get-files regexp))
(relative-paths (mapcar #'file-relative-name files)))
(dired (cons (format "prot-flat-dired for `%s'" regexp) relative-paths))
(error "No files matching `%s'" regexp)))
(defvar prot-dired-days-prompt-history nil
"Minibuffer history for `prot-dired-days-prompt'.")
(defun prot-dired-days-prompt ()
"Prompt for days and return them as a number."
(let* ((first (car prot-dired-days-prompt-history))
(default (when (stringp first)
(string-to-number first))))
(read-number "Number of days: " default 'prot-dired-days-prompt-history)))
(defun prot-dired--get-last-modified (files days)
"Return list of FILES last modified since DAYS."
(seq-filter
(lambda (file)
(and-let* ((attributes (file-attributes file))
(last-modified (nth 5 attributes))
(last-modified-seconds (time-to-seconds last-modified))
(current-time (current-time))
(current-time-seconds (time-to-seconds current-time))
(delta-seconds (* days 24 60 60))
(oldest-seconds (- current-time-seconds delta-seconds))
(_ (>= last-modified-seconds oldest-seconds)))))
files))
;;;###autoload
(defun prot-dired-search-flat-list-since-days (regexp days)
"Return Dired buffer with files matching REGEXP up to DAYS since last modification.
Perform the search recursively from the current directory."
(interactive
(list
(prot-dired-regexp-prompt)
(prot-dired-days-prompt)))
(if-let* ((files (prot-dired--get-files regexp)))
(if-let* ((files-filtered (prot-dired--get-last-modified files days))
(relative-paths (mapcar #'file-relative-name files-filtered)))
(dired (cons (format "prot-flat-dired since %d days for `%s'" days regexp) relative-paths))
(error "No files last modified within the last %d days" days))
(error "No files matching `%s'" regexp)))
;;;; General commands
;; NOTE 2023-06-27: This user option is quick-and-dirty. I prefer not
;; to have an option at all and simply do the right thing based on
;; `dired-guess-shell-alist-user'.
@ -92,51 +171,36 @@ Restore the buffer with \\<dired-mode-map>`\\[revert-buffer]'."
(dired-do-kill-lines)
(add-to-history 'prot-dired--limit-hist regexp))
(defvar prot-dired--find-grep-hist '()
"Minibuffer history for `prot-dired-grep-marked-files'.")
(defvar prot-dired-grep-marked-files-history nil
"Minibuffer history for `prot-dired-grep-marked-files-prompt'.")
(defun prot-dired-grep-marked-files-prompt ()
"Prompt for string to search for with `prot-dired-grep-marked-files'."
(read-string
"grep for PATTERN in marked files: "
nil 'prot-dired-grep-marked-files-history))
;; Also see `prot-search-grep' from prot-search.el.
;;;###autoload
(defun prot-dired-grep-marked-files (regexp &optional arg)
"Run `find' with `grep' for REGEXP on marked files.
When no files are marked or when just a single one is marked,
search the entire directory instead.
With optional prefix ARG target a single marked file.
We assume that there is no point in marking a single file and
running find+grep on its contents. Visit it and call `occur' or
run grep directly on it without the whole find part."
(defun prot-dired-grep-marked-files (files regexp)
"Run `find' with `grep' for REGEXP on marked Dired FILES.
If FILES contains directories, search through them recursively.
Ignore .git directories."
(interactive
(list
(read-string "grep for PATTERN (marked files OR current directory): " nil 'prot-dired--find-grep-hist)
current-prefix-arg)
dired-mode)
(when-let* ((marks (dired-get-marked-files 'no-dir))
(files (mapconcat #'identity marks " "))
(args (if (or arg (length> marks 1))
;; Thanks to Sean Whitton for pointing out an
;; earlier superfluity of mine: we do not need
;; to call grep through find when we already
;; know the files we want to search in. Check
;; Sean's dotfiles:
;; <https://git.spwhitton.name/dotfiles>.
;;
;; Any other errors or omissions are my own.
(format "grep -nH --color=auto %s %s" (shell-quote-argument regexp) files)
(concat
"find . -not " (shell-quote-argument "(")
" -wholename " (shell-quote-argument "*/.git*")
" -prune " (shell-quote-argument ")")
" -type f"
" -exec grep -nHE --color=auto " regexp " "
(shell-quote-argument "{}")
" " (shell-quote-argument ";") " "))))
(compilation-start
args
'grep-mode
(lambda (mode) (format "*prot-dired-find-%s for '%s'" mode regexp))
t)))
(if-let* ((marks (dired-get-marked-files)))
(list marks (prot-dired-grep-marked-files-prompt))
(user-error "Mark one or more files or directories")))
(compilation-start
(concat
"find " (mapconcat #'shell-quote-argument files " ")
" -path " (shell-quote-argument "*/.git") " -prune "
" -o -type f "
" -exec grep -nHER --color=auto " (shell-quote-argument regexp) " "
(shell-quote-argument "{}")
" " (shell-quote-argument "+"))
'grep-mode
(lambda (_mode) (format "*prot-dired-grep-marked for `%s'*" regexp))
t))
;;;; Subdir extras and Imenu setup
@ -180,7 +244,7 @@ For more on such headings, read `dired-maybe-insert-subdir'."
(while (and (prot-dired-subdirectory-previous)
(not (equal (dired-current-directory)
(expand-file-name default-directory))))
(dired-kill-subdir)))
(dired-kill-subdir)))
(autoload 'cl-remove-if-not "cl-seq")
@ -208,10 +272,10 @@ For more on such headings, read `dired-maybe-insert-subdir'."
When items are marked, insert those which are subsirectories of
the current directory. Ignore regular files.
If no marks are active and point is on a subdirectory line,
If no files are active and point is on a subdirectory line,
insert it directly.
If no marks are active and point is not on a subdirectory line,
If no files are active and point is not on a subdirectory line,
prompt for a subdirectory using completion.
With optional ARG as a single prefix (`\\[universal-argument]')

View file

@ -0,0 +1,76 @@
;;; prot-ediff.el --- Ediff extensions for my dotemacs -*- lexical-binding: t -*-
;; Copyright (C) 2025-2026 Protesilaos
;; Author: Protesilaos <info@protesilaos.com>
;; URL: https://protesilaos.com/emacs/dotemacs
;; Version: 0.1.0
;; Package-Requires: ((emacs "30.1"))
;; This file is NOT part of GNU Emacs.
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;;
;; Extensions for Ediff, intended for use in my Emacs setup:
;; https://protesilaos.com/emacs/dotemacs.
;;; Code:
(require 'ediff)
(defun prot-ediff-store-layout ()
"Store current frame window configuration as a frame parameter.
Add this function to the `ediff-before-setup-hook'.
Also see `prot-ediff-restore-layout'."
(let ((frame (selected-frame)))
(set-frame-parameter
frame
'prot-ediff-last-layout
(current-window-configuration frame))))
(defun prot-ediff-restore-layout ()
"Restore the frame's window configuration.
Add this function to the `ediff-quit-hook'.
Also see `prot-ediff-store-layout'."
(if-let* ((layout (frame-parameter (selected-frame) 'prot-ediff-last-layout)))
(set-window-configuration layout)
;; We do not signal a `user-error' here because that would prevent
;; `ediff-quit' from closing the Ediff session.
(message "No Ediff window configuration for the current frame")))
(defun prot-ediff-visible-buffers-2 ()
"Run ediff on the buffers displayed in the current frame's two windows."
(interactive)
(if-let* ((windows (window-list))
(_ (= (length windows) 2))
(buffers (mapcar #'window-buffer windows)))
(pcase-let ((`(,first ,second) buffers))
(ediff-buffers first second))
(user-error "Can only operate on two windows")))
(defun prot-ediff-visible-buffers-3 ()
"Run ediff on the buffers displayed in the current frame's three windows."
(interactive)
(if-let* ((windows (window-list))
(_ (= (length windows) 3))
(buffers (mapcar #'window-buffer windows)))
(pcase-let ((`(,first ,second ,third) buffers))
(ediff-buffers3 first second third))
(user-error "Can only operate on three windows")))
(provide 'prot-ediff)
;;; prot-ediff.el ends here

View file

@ -1,184 +0,0 @@
;;; prot-elfeed.el --- Elfeed extensions for my dotemacs -*- lexical-binding: t -*-
;; Copyright (C) 2021-2023 Protesilaos Stavrou
;; Author: Protesilaos Stavrou <info@protesilaos.com>
;; URL: https://protesilaos.com/emacs/dotemacs
;; Version: 0.1.0
;; Package-Requires: ((emacs "30.1"))
;; This file is NOT part of GNU Emacs.
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; NOTE 2022-06-08: This is old code. There are things I would like to
;; improve.
;;
;; Extensions for Elfeed, intended for use in my Emacs setup:
;; https://protesilaos.com/emacs/dotemacs.
;;
;; Remember that every piece of Elisp that I write is for my own
;; educational and recreational purposes. I am not a programmer and I
;; do not recommend that you copy any of this if you are not certain of
;; what it does.
;;; Code:
(eval-when-compile (require 'subr-x))
(require 'elfeed nil t)
(require 'url-util)
(require 'prot-common)
(defgroup prot-elfeed ()
"Personal extensions for Elfeed."
:group 'elfeed)
(defcustom prot-elfeed-feeds-file
(thread-last user-emacs-directory (expand-file-name "feeds.el.gpg"))
"Path to file with `elfeed-feeds'."
:type 'string
:group 'prot-elfeed)
(defcustom prot-elfeed-archives-directory "~/Documents/feeds/"
"Path to directory for storing Elfeed entries."
:type 'string
:group 'prot-elfeed)
(defcustom prot-elfeed-tag-faces nil
"Add faces for certain tags.
The tags are: critical, important, personal."
:type 'boolean
:group 'prot-elfeed)
(defcustom prot-elfeed-search-tags '(critical important personal)
"List of user-defined tags.
Used by `prot-elfeed-toggle-tag'."
:type 'list
:group 'prot-elfeed)
(defface prot-elfeed-entry-critical '((t :inherit font-lock-warning-face))
"Face for Elfeed entries tagged with `critical'.")
(defface prot-elfeed-entry-important '((t :inherit font-lock-constant-face))
"Face for Elfeed entries tagged with `important'.")
(defface prot-elfeed-entry-personal '((t :inherit font-lock-variable-name-face))
"Face for Elfeed entries tagged with `personal'.")
;;;; Utilities
;;;###autoload
(defun prot-elfeed-load-feeds ()
"Load file containing the `elfeed-feeds' list.
Add this to `elfeed-search-mode-hook'."
(let ((feeds prot-elfeed-feeds-file))
(if (file-exists-p feeds)
(load-file feeds)
(user-error "Missing feeds' file"))))
(defvar elfeed-search-face-alist)
;;;###autoload
(defun prot-elfeed-fontify-tags ()
"Expand Elfeed faces if `prot-elfeed-tag-faces' is non-nil."
(if prot-elfeed-tag-faces
(setq elfeed-search-face-alist
'((critical prot-elfeed-entry-critical)
(important prot-elfeed-entry-important)
(personal prot-elfeed-entry-personal)
(unread elfeed-search-unread-title-face)))
(setq elfeed-search-face-alist
'((unread elfeed-search-unread-title-face)))))
(defvar prot-elfeed--tag-hist '()
"History of inputs for `prot-elfeed-toggle-tag'.")
(defun prot-elfeed--character-prompt (tags)
"Helper of `prot-elfeed-toggle-tag' to read TAGS."
(let ((def (car prot-elfeed--tag-hist)))
(completing-read
(format "Toggle tag [%s]: " def)
tags nil t nil 'prot-elfeed--tag-hist def)))
(defvar elfeed-show-entry)
(declare-function elfeed-tagged-p "elfeed")
(declare-function elfeed-search-toggle-all "elfeed")
(declare-function elfeed-show-tag "elfeed")
(declare-function elfeed-show-untag "elfeed")
;;;###autoload
(defun prot-elfeed-toggle-tag (tag)
"Toggle TAG for the current item.
When the region is active in the `elfeed-search-mode' buffer, all
entries encompassed by it are affected. Otherwise the item at
point is the target. For `elfeed-show-mode', the current entry
is always the target.
The list of tags is provided by `prot-elfeed-search-tags'."
(interactive
(list
(intern
(prot-elfeed--character-prompt prot-elfeed-search-tags))))
(if (derived-mode-p 'elfeed-show-mode)
(if (elfeed-tagged-p tag elfeed-show-entry)
(elfeed-show-untag tag)
(elfeed-show-tag tag))
(elfeed-search-toggle-all tag)))
(defvar elfeed-show-truncate-long-urls)
(declare-function elfeed-entry-title "elfeed")
(declare-function elfeed-show-refresh "elfeed")
;;;; General commands
(defvar elfeed-search-filter-active)
(defvar elfeed-search-filter)
(declare-function elfeed-db-get-all-tags "elfeed")
(declare-function elfeed-search-update "elfeed")
(declare-function elfeed-search-clear-filter "elfeed")
(defun prot-elfeed--format-tags (tags sign)
"Prefix SIGN to each tag in TAGS."
(mapcar (lambda (tag)
(format "%s%s" sign tag))
tags))
;;;###autoload
(defun prot-elfeed-search-tag-filter ()
"Filter Elfeed search buffer by tags using completion.
Completion accepts multiple inputs, delimited by `crm-separator'.
Arbitrary input is also possible, but you may have to exit the
minibuffer with something like `exit-minibuffer'."
(interactive)
(unwind-protect
(elfeed-search-clear-filter)
(let* ((elfeed-search-filter-active :live)
(db-tags (elfeed-db-get-all-tags))
(plus-tags (prot-elfeed--format-tags db-tags "+"))
(minus-tags (prot-elfeed--format-tags db-tags "-"))
(all-tags (delete-dups (append plus-tags minus-tags)))
(tags (completing-read-multiple
"Apply one or more tags: "
all-tags #'prot-common-crm-exclude-selected-p t))
(input (string-join `(,elfeed-search-filter ,@tags) " ")))
(setq elfeed-search-filter input))
(elfeed-search-update :force)))
(provide 'prot-elfeed)
;;; prot-elfeed.el ends here

View file

@ -0,0 +1,94 @@
;;; prot-elisp.el --- Emacs Lisp extras for my dotemacs -*- lexical-binding: t -*-
;; Copyright (C) 2025-2026 Protesilaos
;; Author: Protesilaos <info@protesilaos.com>
;; URL: https://protesilaos.com/emacs/dotemacs
;; Version: 0.1.0
;; Package-Requires: ((emacs "30.1"))
;; This file is NOT part of GNU Emacs.
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;;
;; Extensions for Emacs Lisp, intended for use in my Emacs setup:
;; https://protesilaos.com/emacs/dotemacs.
;;; Code:
;;;###autoload
(defun prot-elisp-eval-and-print-last-sexp ()
"Evaluate and print expression before point like `eval-print-last-sexp'.
Prepend a comment to the return value. Also copy the return value to
the `kill-ring' and set the mark to where point was before inserting the
return value."
(declare (interactive-only t))
(interactive)
(if-let* ((string (thing-at-point 'sexp :no-properties))
(_ (not (string-prefix-p ";" string)))
(expression (read string)))
(let ((return-value (eval expression)))
(kill-new (format "%S" return-value))
(message "Copied: `%S'" return-value)
(push-mark (point))
(insert (format "\n%S\n" return-value))
(string-insert-rectangle (+ (mark) 1) (- (point) 1) ";; => "))
(user-error "No expression at point")))
(defvar-keymap prot-elisp-macroexpand-mode-map
:doc "keymap for `prot-elisp-macroexpand-mode'."
:parent special-mode-map)
(define-derived-mode prot-elisp-macroexpand-mode emacs-lisp-mode "MacroExpand"
"Like `emacs-lisp-mode' but for macroexpanded forms."
:interactive nil
(read-only-mode 1)
(display-line-numbers-mode 1))
;;;###autoload
(defun prot-elisp-pp-macroexpand-last-sexp ()
"Like `pp-macroexpand-last-sexp' but with a generic `display-buffer'.
Now use `display-buffer-alist' like the Lisp gods intended."
(declare (interactive-only t))
(interactive)
(if-let* ((thing (thing-at-point 'sexp :no-properties))
(expression (read thing))
(buffer (get-buffer-create "*prot-elisp-macroexpand*"))
(inhibit-read-only t))
(progn
(with-current-buffer buffer
(erase-buffer)
(insert (format "%S" (macroexpand-1 expression)))
(prot-elisp-macroexpand-mode)
(pp-buffer))
(display-buffer buffer))
(user-error "No expression to macroexpand")))
;;;###autoload
(defun prot-elisp-copy-current-definition ()
"Copy the symbol of the definition around point."
(interactive)
(if-let* ((text (save-excursion
(beginning-of-defun)
(buffer-substring-no-properties (line-beginning-position) (line-end-position))))
(definition (replace-regexp-in-string "(.+?\s+\\(.+?\\)\s+.*" "\\1" text)))
(progn
(kill-new definition)
(message "Copied `%s'" definition))
(message "Cannot find definition near point")))
(provide 'prot-elisp)
;;; prot-elisp.el ends here

View file

@ -1,8 +1,8 @@
;;; prot-embark.el --- Custom Embark keymaps -*- lexical-binding: t -*-
;; Copyright (C) 2023 Protesilaos Stavrou
;; Copyright (C) 2023-2026 Protesilaos
;; Author: Protesilaos Stavrou <info@protesilaos.com>
;; Author: Protesilaos <info@protesilaos.com>
;; URL: https://protesilaos.com/emacs/dotemacs
;; Version: 0.1.0
;; Package-Requires: ((emacs "30.1") (embark "0.23"))

View file

@ -1,10 +1,9 @@
;;; prot-eww.el --- Extensions for EWW -*- lexical-binding: t -*-
;; Copyright (C) 2021-2023 Protesilaos Stavrou, Abhiseck Paira
;; Copyright (C) 2021-2026 Protesilaos
;; Author: Protesilaos Stavrou <info@protesilaos.com>
;; Abhiseck Paira <abhiseckpaira@disroot.org>
;; Maintainer: Protesilaos Stavrou <info@protesilaos.com>
;; Author: Protesilaos <info@protesilaos.com>
;; Maintainer: Protesilaos <info@protesilaos.com>
;; URL: https://protesilaos.com/emacs/dotemacs
;; Version: 0.1.0
;; Package-Requires: ((emacs "30.1"))
@ -28,359 +27,79 @@
;;
;; Extensions for the eww, intended for my Emacs setup:
;; <https://protesilaos.com/emacs/dotemacs/>.
;;
;; Remember that every piece of Elisp that I write is for my own
;; educational and recreational purposes. I am not a programmer and I
;; do not recommend that you copy any of this if you are not certain of
;; what it does.
;; XXX NOTE XXX 2023-05-19: Much of this code is severely out of date.
;; I plan to review it. DO NOT USE!!!
;;; Code:
(require 'shr)
(require 'eww)
(require 'url-parse)
(require 'prot-common)
(defgroup prot-eww ()
(defgroup prot-eww nil
"Tweaks for EWW."
:group 'eww)
;;;; Basic setup
;; TODO 2021-10-15: Deprecate this in favour of what we added to Emacs29.
;; <https://protesilaos.com/codelog/2021-10-15-emacs-29-eww-rename-buffers/>.
(defun prot-eww--rename-buffer ()
"Rename EWW buffer using page title or URL.
To be used by `eww-after-render-hook'."
(let ((name (if (equal "" (plist-get eww-data :title))
(plist-get eww-data :url)
(plist-get eww-data :title))))
(rename-buffer (format "*%s # eww*" name) t)))
(add-hook 'eww-after-render-hook #'prot-eww--rename-buffer)
(advice-add 'eww-back-url :after #'prot-eww--rename-buffer)
(advice-add 'eww-forward-url :after #'prot-eww--rename-buffer)
;;;; History extras
(defvar prot-eww-visited-history '()
"History of visited URLs.")
(defcustom prot-eww-save-history-file
(locate-user-emacs-file "prot-eww-visited-history")
"File to save the value of `prot-eww-visited-history'."
:type 'file
:group 'prot-eww)
(defcustom prot-eww-save-visited-history nil
"Whether to save `prot-eww-visited-history'.
If non-nil, save the value of `prot-eww-visited-history' in
`prot-eww-save-history-file'."
:type 'boolean
:group 'prot-eww)
(defcustom prot-eww-list-history-buffer "*prot-eww-history*"
"Name of buffer for `prot-eww-list-history'."
:type 'string
:group 'prot-eww)
;; These history related functions are adapted from eww.
(defun prot-eww--save-visited-history ()
"Save the value of `prot-eww-visited-history' in a file.
The file is determined by the variable `prot-eww-save-history-file'."
(when prot-eww-save-visited-history
(with-temp-file prot-eww-save-history-file
(insert (concat ";; Auto-generated file;"
" don't edit -*- mode: lisp-data -*-\n"))
(pp prot-eww-visited-history (current-buffer)))))
(defun prot-eww--read-visited-history (&optional error-out)
"Read history from `prot-eww-save-history-file'.
If ERROR-OUT, signal `user-error' if there is no history."
(when prot-eww-save-visited-history
(let ((file prot-eww-save-history-file))
(setq prot-eww-visited-history
(unless (zerop
(or (file-attribute-size (file-attributes file))
0))
(with-temp-buffer
(insert-file-contents file)
(read (current-buffer)))))
(when (and error-out (not prot-eww-visited-history))
(user-error "No history is defined")))))
(unless prot-eww-visited-history
(prot-eww--read-visited-history t))
(defun prot-eww--history-prepare ()
"Prepare dedicated buffer for browsing history."
(set-buffer (get-buffer-create prot-eww-list-history-buffer))
(prot-eww-history-mode)
(let ((inhibit-read-only t)
start)
(erase-buffer)
(setq-local header-line-format
"EWW Browsing History (prot-eww)")
(dolist (history prot-eww-visited-history)
(setq start (point))
(insert (format "%s" history) "\n")
(put-text-property start (1+ start) 'prot-eww-history history))
(goto-char (point-min))))
;;;###autoload
(defun prot-eww-list-history ()
"Display `prot-eww-visited-history' in a dedicated buffer.
This is a replacement for `eww-list-histories' (or equivalent),
as it can combine URLs in the Gopher or Gemini protocols."
(interactive)
(when prot-eww-visited-history
(prot-eww--save-visited-history))
(prot-eww--read-visited-history t)
(pop-to-buffer prot-eww-list-history-buffer)
(prot-eww--history-prepare))
(defvar prot-eww-history-kill-ring nil
"Store the killed history element.")
(defun prot-eww-history-kill ()
"Kill the current history."
(interactive)
(let* ((start (line-beginning-position))
(history (get-text-property start 'prot-eww-history))
(inhibit-read-only t))
(unless history
(user-error "No history on the current line"))
(forward-line 1)
(push (buffer-substring start (point))
prot-eww-history-kill-ring)
(delete-region start (point))
(setq prot-eww-visited-history (delq history
prot-eww-visited-history))
(prot-eww--save-visited-history)))
(defun prot-eww-history-yank ()
"Yank a previously killed history to the current line."
(interactive)
(unless prot-eww-history-kill-ring
(user-error "No previously killed history"))
(beginning-of-line)
(let ((inhibit-read-only t)
(start (point))
history)
(insert (pop prot-eww-history-kill-ring))
(setq history (get-text-property start 'prot-eww-history))
(if (= start (point-min))
(push history prot-eww-visited-history)
(let ((line (count-lines start (point))))
(setcdr (nthcdr (1- line) prot-eww-visited-history)
(cons history (nthcdr line
prot-eww-visited-history)))))
(prot-eww--save-visited-history)))
(defun prot-eww-history-browse ()
"Browse the history under point."
(interactive)
(let ((history (get-text-property (line-beginning-position)
'prot-eww-history)))
(unless history
(user-error "No history on the current line"))
(quit-window)
(prot-eww history)))
(defvar prot-eww-history-mode-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "C-k") 'prot-eww-history-kill)
(define-key map (kbd "C-y") 'prot-eww-history-yank)
(define-key map (kbd "<RET>") 'prot-eww-history-browse)
(easy-menu-define nil map
"Menu for `prot-eww-history-mode-map'."
'("prot-eww history"
["Exit" quit-window t]
["Browse" prot-eww-history-browse
:active (get-text-property (line-beginning-position)
'prot-eww-history)]
["Kill" prot-eww-history-kill
:active (get-text-property (line-beginning-position)
'prot-eww-history)]
["Yank" prot-eww-history-yank
:active prot-eww-history-kill-ring]))
map))
(define-derived-mode prot-eww-history-mode
special-mode
"prot-eww-history"
"Mode for listing history.
\\{prot-eww-history-mode-map}"
(buffer-disable-undo)
(setq truncate-lines t))
(defun prot-eww--record-history ()
"Store URL in `prot-eww-visited-history'.
To be used by `eww-after-render-hook'."
(let ((url (plist-get eww-data :url)))
(add-to-history 'prot-eww-visited-history url)))
(add-hook 'eww-after-render-hook #'prot-eww--record-history)
(advice-add 'eww-back-url :after #'prot-eww--record-history)
(advice-add 'eww-forward-url :after #'prot-eww--record-history)
;; Is there a better function to add this advice?
;;;; Commands
;; handler that browse-url calls.
(defun prot-eww--get-current-url ()
"Return the current-page's URL."
(when (eq major-mode 'eww-mode)
(plist-get eww-data :url)))
;;;###autoload
(defun prot-eww (url &optional arg)
"Pass URL to appropriate client.
With optional ARG, use a new buffer."
(interactive (list (browse-url-interactive-arg "URL: ") current-prefix-arg))
(eww url arg))
;;;###autoload
(defun prot-eww-browse-dwim (url &optional arg)
"Visit a URL, maybe from `eww-prompt-history', with completion.
With optional prefix ARG (\\[universal-argument]) open URL in a
new eww buffer. If URL does not look like a valid link, run a
web query using `eww-search-prefix'.
When called from an eww buffer, provide the current link as
\\<minibuffer-local-map>\\[next-history-element]."
(interactive
(let ((all-history (delete-dups
(append prot-eww-visited-history
eww-prompt-history)))
(current-url (prot-eww--get-current-url)))
(list
(completing-read "Run EWW on: " all-history
nil nil current-url 'eww-prompt-history current-url)
(prefix-numeric-value current-prefix-arg))))
(prot-eww url arg))
;; NOTE 2021-09-08: This uses the EWW-specific bookmarks, NOT those of
;; bookmark.el. Further below I provide integration with the latter,
;; meaning that we must either make this obsolete or make it work with
;; the new system.
;;;###autoload
(defun prot-eww-visit-bookmark (&optional arg)
"Visit bookmarked URL.
With optional prefix ARG (\\[universal-argument]) open URL in a
new EWW buffer."
(interactive "P")
(eww-read-bookmarks)
(let ((list (gensym)))
(dolist (bookmark eww-bookmarks)
(push (plist-get bookmark :url) list))
(if eww-bookmarks
(eww (completing-read "Visit EWW bookmark: " list)
(when arg 4))
(user-error "No bookmarks"))))
(defun prot-eww--capture-url-on-page (&optional position)
"Capture all the links on the current web page.
Return a list of strings. Strings are in the form LABEL @ URL.
When optional argument POSITION is non-nil, include position info
in the strings too, so strings take the form
LABEL @ URL ~ POSITION."
(let (links match)
(defun prot-eww--get-urls ()
"Get all links in the current buffer."
(let ((links nil))
(save-excursion
(goto-char (point-max))
;; NOTE 2021-07-25: The first clause in the `or' is meant to
;; address a bug where if a URL is in `point-min' it does not get
;; captured.
(while (setq match (text-property-search-backward 'shr-url))
(let* ((raw-url (prop-match-value match))
(start-point-prop (prop-match-beginning match))
(end-point-prop (prop-match-end match))
(url (when (stringp raw-url)
(propertize raw-url 'face 'link)))
(label (replace-regexp-in-string "\n" " " ; NOTE 2021-07-25: newlines break completion
(buffer-substring-no-properties
start-point-prop end-point-prop)))
(point start-point-prop)
(line (line-number-at-pos point t))
(column (save-excursion (goto-char point) (current-column)))
(coordinates (propertize
(format "%d,%d (%d)" line column point)
'face 'shadow)))
(when url
(if position
(push (format "%-15s ~ %s @ %s"
coordinates label url)
links)
(push (format "%s @ %s"
label url)
links))))))
links))
(goto-char (point-min))
(while (text-property-search-forward 'face 'shr-link)
(when-let* ((position (point))
(button (button-at position)))
(push
(list position
(button-label button)
(shr-url-at-point nil))
links))))
(nreverse links)))
(defmacro prot-eww-act-visible-window (&rest body)
"Run BODY within narrowed-region.
If region is active run BODY within active region instead.
Return the value of the last form of BODY."
`(save-restriction
(if (use-region-p)
(narrow-to-region (region-beginning) (region-end))
(narrow-to-region (window-start) (window-end)))
,@body))
(defun prot-eww-buffer-url-prompt ()
"Prompt for a url in the current buffer."
(when-let* ((link-data (prot-eww--get-urls))
(candidates (mapcar
(pcase-lambda (`(,position ,name ,_))
(format "%s %s" position name))
link-data))
(table (prot-common-completion-table-no-sort nil candidates))
(selection
(completing-read
(format-prompt "Select link in the current page" nil)
table))
(position (car (split-string selection "\t")))
(number (string-to-number position)))
(assoc number link-data)))
;;;###autoload
(defun prot-eww-visit-url-on-page (&optional arg)
"Visit URL from list of links on the page using completion.
(defun prot-eww-visit-url-on-page (&optional new-buffer)
"Visit URL among those in the current buffer using completion.
With optional NEW-BUFFER as a prefix argument, visit the URL in a new
buffer instead of the current one."
(interactive "P" eww-mode)
(unless (derived-mode-p 'eww-mode)
(user-error "This command only works in an EWW buffer"))
(if-let* ((data (prot-eww-buffer-url-prompt))
(url (nth 2 data)))
(eww url new-buffer)
(error "Cannot find URL in data `%s'" data)))
With optional prefix ARG (\\[universal-argument]) open URL in a
new EWW buffer."
(interactive "P")
(when (derived-mode-p 'eww-mode)
(let* ((links (prot-eww--capture-url-on-page))
(selection (completing-read "Browse URL from page: " links nil t))
(url (replace-regexp-in-string ".*@ " "" selection)))
(eww url (when arg 4)))))
;;;###autoload
(defun prot-eww-jump-to-url-on-page (&optional arg)
"Jump to URL position on the page using completion.
When called without ARG (\\[universal-argument]) get URLs only
from the visible portion of the buffer. But when ARG is provided
consider whole buffer."
(interactive "P")
(when (derived-mode-p 'eww-mode)
(let* ((links
(if arg
(prot-eww--capture-url-on-page t)
(prot-eww-act-visible-window
(prot-eww--capture-url-on-page t))))
(prompt-scope (if arg
(propertize "URL on the page" 'face 'warning)
"visible URL"))
(prompt (format "Jump to %s: " prompt-scope))
(selection (completing-read prompt links nil t))
(position (replace-regexp-in-string "^.*(\\([0-9]+\\))[\s\t]+~" "\\1" selection))
(point (string-to-number position)))
(goto-char point))))
(defun prot-eww-jump-to-url-on-page ()
"Go the position of a URL among those in the current buffer."
(interactive nil eww-mode)
(unless (derived-mode-p 'eww-mode)
(user-error "This command only works in an EWW buffer"))
(if-let* ((data (prot-eww-buffer-url-prompt))
(position (car data)))
(goto-char position)
(error "Cannot position in data `%s'" data)))
(defvar prot-eww--occur-feed-regexp
(concat "\\(rss\\|atom\\)\\+xml.\\(.\\|\n\\)"
".*href=[\"']\\(.*?\\)[\"']")
"Regular expression to match web feeds in HTML source.")
;;;###autoload
(defun prot-eww-find-feed ()
"Produce bespoke buffer with RSS/Atom links from XML source."
(interactive)
(interactive nil eww-mode)
(unless (derived-mode-p 'eww-mode)
(user-error "This command only works in an EWW buffer"))
(let* ((url (or (plist-get eww-data :start)
(plist-get eww-data :contents)
(plist-get eww-data :home)
@ -401,124 +120,12 @@ consider whole buffer."
(let ((inhibit-read-only t)
(base-url (replace-regexp-in-string "\\(.*/\\)[^/]+\\'" "\\1" url)))
(goto-char (point-min))
(unless (re-search-forward prot-common-url-regexp nil t)
(re-search-forward ".*")
(replace-match (concat base-url "\\&"))))))))
;;TODO: Add this variable as user-option, that is, define it with
;;`defcustom' so that users can use the customization interface to
;;modify it.
(defvar prot-eww-search-engines
'((debbugs . (debbugs
"https://debbugs.gnu.org/cgi/bugreport.cgi?bug="
hist-var prot-eww--debbugs-hist))
(wikipedia . (wikipedia
"https://en.m.wikipedia.org/w/index.php?search="
hist-var prot-eww--wikipedia-hist))
(archwiki . (archwiki
"https://wiki.archlinux.org/index.php?search="
hist-var prot-eww--archwiki-hist))
(aur . (aur "https://aur.archlinux.org/packages/?K="
hist-var prot-eww--aur-hist)))
"Alist of Plist of web search engines related data.
From now on refer to this type of data as APLIST. Each element
of APLIST is (KEY . VALUE) pair. KEY is a symbol specifying
search engine name. The VALUE is property list.
The plist has two key-value pairs. K1 is the same symbol has KEY
and V1 is search string of the search engine.
K2 is the symbol 'hist-var', V2 is also a symbol that has a format
'prot-eww--K1-hist'.
NOTE: If you modify this variable after `prot-eww' is loaded you
need to run the following code after modification:
(prot-eww--define-hist-var prot-eww-search-engines)")
;; Below 's-string' is short for 'search-string'. For wikipedia which
;; is this string: "https://en.m.wikipedia.org/w/index.php?search=". I
;; use this name because I don't know it's proper name.
;; Define constructor and selectors functions to access
;; `prot-eww-search-engines'.
;; the constructor
(defun prot-eww--cons-search-engines (name s-string)
"Include a new Alist element.
The alist element is added to variable `prot-eww-search-engines'.
NAME should be symbol representing the search engine. S-STRING
should be string, which is specific to named search engine."
(let ((my-plist `(,name ,s-string))
(hist-var-name (format "prot-eww--%s-hist"
(symbol-name name))))
(plist-put my-plist 'hist-var (intern hist-var-name))
(let ((my-alist (cons name my-plist)))
(add-to-list 'prot-eww-search-engines my-alist))))
;; Selectors definitions start
(defun prot-eww--select-hist-name (aplist engine-name)
"Get hist-var-name from APLIST of ENGINE-NAME."
(let ((hist-var-name (plist-get
(alist-get engine-name aplist)
'hist-var)))
hist-var-name))
(defun prot-eww--select-engine-names (aplist)
"Return a list of search-engine names from APLIST.
Each value of the list is a string."
(mapcar (lambda (x) (format "%s" (car x)))
aplist))
(defun prot-eww--select-s-string (aplist engine-name)
"Return the search-string for specified ENGINE-NAME from APLIST."
(plist-get
(alist-get engine-name aplist)
engine-name))
;; Selector definitions end here.
(defun prot-eww--define-hist-var (aplist)
"Initialize APLIST hist-variables to empty list; return nil."
(let ((engine-names
(prot-eww--select-engine-names aplist)))
(dolist (engine engine-names)
(let ((hist-var-name
(prot-eww--select-hist-name aplist
(intern engine))))
(set hist-var-name '())))))
(prot-eww--define-hist-var prot-eww-search-engines)
;;;###autoload
(defun prot-eww-search-engine (engine s-term &optional arg)
"Search S-TERM using ENGINE.
ENGINE is an assossiation defined in `prot-eww-search-engines'.
With optional prefix ARG (\\[universal-argument]) open the search
result in a new buffer."
(interactive
(let* ((engine-list (prot-eww--select-engine-names
prot-eww-search-engines))
(engine-name (completing-read
"Search with: " engine-list nil t nil
'prot-eww--engine-hist))
(history-list (prot-eww--select-hist-name
prot-eww-search-engines
(intern engine-name)))
(search-term (read-string
"Search for: " nil history-list)))
(list engine-name search-term
(prefix-numeric-value current-prefix-arg))))
(let* ((s-string
(prot-eww--select-s-string prot-eww-search-engines
(intern engine)))
(eww-pass (format "%s%s" s-string s-term))
(history-list (prot-eww--select-hist-name
prot-eww-search-engines
(intern engine))))
(add-to-history history-list s-term)
(eww eww-pass arg)))
(while (< (point) (point-max))
(goto-char (line-beginning-position))
(when (and (looking-at prot-common-url-regexp)
(not (looking-at (format "%s.*" url))))
(insert base-url))
(forward-line 1)))))))
;;;###autoload
(defun prot-eww-open-in-other-window ()
@ -527,134 +134,5 @@ result in a new buffer."
(other-window-prefix) ; For emacs28 -- it's a hack, but why not?
(eww-open-in-new-buffer))
;;;###autoload
(defun prot-eww-readable ()
"Use more opinionated `eww-readable'.
Set width is set to `current-fill-column'. Adjust size of
images."
(interactive)
(let ((shr-width (current-fill-column))
(shr-max-image-proportion 0.35))
(eww-readable)))
;; NOTE 2021-09-08: This uses the EWW-specific bookmarks, NOT those of
;; bookmark.el. Further below I provide integration with the latter,
;; meaning that we must either make this obsolete or make it work with
;; the new system.
;;;###autoload
(defun prot-eww-bookmark-page (title)
"Add eww bookmark named with TITLE."
(interactive
(list
(read-string "Set bookmark title: " (plist-get eww-data :title))))
(plist-put eww-data :title title)
(eww-add-bookmark))
(defvar prot-eww--punctuation-regexp "[][{}!@#$%^&*()_=+'\"?,.\|;:~`‘’“”]*"
"Regular expression of punctionation that should be removed.")
(defun prot-eww--slug-no-punct (str)
"Convert STR to a file name slug."
(replace-regexp-in-string prot-eww--punctuation-regexp "" str))
(defun prot-eww--slug-hyphenate (str)
"Replace spaces with hyphens in STR.
Also replace multiple hyphens with a single one and remove any
trailing hyphen."
(replace-regexp-in-string
"-$" ""
(replace-regexp-in-string
"-\\{2,\\}" "-"
(replace-regexp-in-string "--+\\|\s+" "-" str))))
(defun prot-eww--sluggify (str)
"Make STR an appropriate file name slug."
(downcase (prot-eww--slug-hyphenate (prot-eww--slug-no-punct str))))
;;;###autoload
(defun prot-eww-download-html (name)
"Download web page and call the file with NAME."
(interactive
(list
(prot-eww--sluggify
(read-string "Set downloaded file name: " (plist-get eww-data :title)))))
(let* ((path (thread-last eww-download-directory
(expand-file-name
(concat (format-time-string "%Y%m%d_%H%M%S") "--" name ".html"))))
(out (prot-common-shell-command-with-exit-code-and-output
"wget" "-q" (format "%s" (plist-get eww-data :url))
"-O" (format "%s" (shell-quote-argument path)))))
(if (= (car out) 0)
(message "Downloaded page at %s" path)
(message "Error downloading page: %s" (cdr out)))))
(defun prot-eww--kill-buffers-when (predicate)
"Kill buffers when PREDICATE is non-nil.
Loop through the buffer list, calling PREDICATE with each buffer.
When calling PREDICATE with a buffer returns non-nil, kill that
buffer.
PREDICATE must be function that takes buffer-object as the one
and only argument. It should return nil or non-nil."
(let ((list-buffers (buffer-list)))
(dolist (buffer list-buffers)
(when (funcall predicate buffer)
(kill-buffer buffer)))))
(defun prot-eww--kill-eww-buffers-p (buffer)
"Predicate function. Return nil or non-nil.
Take BUFFER, make it current, check if it has 'eww-mode' as the
`major-mode' or if its major mode is derived from `special-mode'
and has \"eww\" in the buffer-name. Then return non-nil."
(let ((case-fold-search t)) ; ignore case
(with-current-buffer buffer
(or (eq major-mode 'eww-mode)
(and (derived-mode-p 'special-mode)
(string-match "\\*.*eww.*\\*" (buffer-name)))))))
(defun prot-eww-kill-eww-buffers ()
"Kill all EWW buffers.
Also kill special buffers made by EWW for example buffers like
\"*eww-bookmarks*\", \"*eww-history*\" etc."
(prot-eww--kill-buffers-when 'prot-eww--kill-eww-buffers-p))
(defcustom prot-eww-delete-cookies t
"If non-nil delete cookies when `prot-eww-quit' is called."
:type 'boolean
:group 'prot-eww)
(defun prot-eww-delete-cookies ()
"Delete cookies from the cookie file."
(when prot-eww-delete-cookies
(url-cookie-delete-cookies)))
;; TODO: Make it defcustom
(defvar prot-eww-quit-hook nil
"Run this hook when `prot-eww-quit' is called.")
;; Populate the hook with these functions.
(dolist (func '(prot-eww-delete-cookies
prot-eww-kill-eww-buffers
prot-eww--save-visited-history))
(add-hook 'prot-eww-quit-hook func))
;;;###autoload
(defun prot-eww-quit ()
"Quit eww, kill all its buffers, delete all cookies.
As a final step, save `prot-eww-visited-history' to a file (see
`prot-eww-save-history-file')."
(interactive)
(if prot-eww-save-visited-history
(when (y-or-n-p "Are you sure you want to quit eww? ")
(run-hooks 'prot-eww-quit-hook))
;;
;; Now users have full control what `prot-eww-quit' does, by
;; modifying `prot-eww-quit-hook'.
(when (yes-or-no-p "Are you sure you want to quit eww?")
(run-hooks 'prot-eww-quit-hook))))
(provide 'prot-eww)
;;; prot-eww.el ends here

View file

@ -0,0 +1,363 @@
;;; prot-icons.el --- Get characters, icons, and symbols for things -*- lexical-binding: t -*-
;; Copyright (C) 2025-2026 Protesilaos
;; Author: Protesilaos <info@protesilaos.com>
;; Maintainer: Protesilaos <info@protesilaos.com>
;; URL: https://protesilaos.com/emacs/dotemacs
;; Version: 0.1.0
;; Package-Requires: ((emacs "30.1"))
;; This file is NOT part of GNU Emacs.
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or (at
;; your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;;
;; Extensions to get icons and symbols for files, buffers, or related.
;; Intended for my Emacs setup: <https://protesilaos.com/emacs/dotemacs/>.
;;; Code:
(require 'prot-common)
(defgroup prot-icons nil
"Get characters, icons, and symbols for things."
:group 'convenience)
(defcustom prot-icons-style 'symbolic
"The style of the icons.
- `alphabetic' expresses icons using alphabetic characters.
- `emoji' represents icons as emoji characters.
- `symbolic' uses Unicode symbols for the icons.
Any other value has the same meaning as `text'."
:type '(choice
(const :tag "Alphabetic characters" alphabetic)
(const :tag "Emoji characters" emoji)
(const :tag "Unicode symbols" symbolic)))
(defface prot-icons-icon
'((t :inherit (bold fixed-pitch)))
"Basic attributes for an icon."
:group 'prot-icons)
(defface prot-icons-red
'((default :inherit prot-icons-icon)
(((class color) (min-colors 88) (background light))
:foreground "#aa3232")
(((class color) (min-colors 88) (background dark))
:foreground "#f06464")
(t :foreground "red"))
"Face for icons."
:group 'prot-icons)
(defface prot-icons-green
'((default :inherit prot-icons-icon)
(((class color) (min-colors 88) (background light))
:foreground "#107010")
(((class color) (min-colors 88) (background dark))
:foreground "#33bb33")
(t :foreground "green"))
"Face for icons."
:group 'prot-icons)
(defface prot-icons-yellow
'((default :inherit prot-icons-icon)
(((class color) (min-colors 88) (background light))
:foreground "#605000")
(((class color) (min-colors 88) (background dark))
:foreground "#e0a055")
(t :foreground "yellow"))
"Face for icons."
:group 'prot-icons)
(defface prot-icons-blue
'((default :inherit prot-icons-icon)
(((class color) (min-colors 88) (background light))
:foreground "#223399")
(((class color) (min-colors 88) (background dark))
:foreground "#5599ff")
(t :foreground "blue"))
"Face for icons."
:group 'prot-icons)
(defface prot-icons-magenta
'((default :inherit prot-icons-icon)
(((class color) (min-colors 88) (background light))
:foreground "#8f2270")
(((class color) (min-colors 88) (background dark))
:foreground "#ee70aa")
(t :foreground "magenta"))
"Face for icons."
:group 'prot-icons)
(defface prot-icons-cyan
'((default :inherit prot-icons-icon)
(((class color) (min-colors 88) (background light))
:foreground "#226067")
(((class color) (min-colors 88) (background dark))
:foreground "#77b0c0")
(t :foreground "cyan"))
"Face for icons."
:group 'prot-icons)
(defface prot-icons-gray
'((default :inherit prot-icons-icon)
(((class color) (min-colors 88) (background light))
:foreground "gray30")
(((class color) (min-colors 88) (background dark))
:foreground "gray70")
(t :foreground "gray"))
"Face for icons."
:group 'prot-icons)
(defface prot-icons-directory
'((t :inherit (prot-icons-icon dired-directory)))
"Face for icons."
:group 'prot-icons)
(defvar prot-icons-alphabetic
'((dired-mode "|*" prot-icons-directory)
(archive-mode "|@" prot-icons-directory)
(diff-mode "" prot-icons-yellow) ; διαφορά
(prog-mode "" prot-icons-magenta) ; πρόγραμμα
(conf-mode "" prot-icons-gray) ; πρόγραμμα
(text-mode ">Α" prot-icons-green) ; αλφάβητο
(comint-mode ">_" prot-icons-gray)
(read-only "" prot-icons-red)
(document "" prot-icons-red) ; σύγγραμμα
(audio ">Η" prot-icons-cyan) ; ήχος
(image ">Ε" prot-icons-yellow) ; εικόνα
(video ">Κ" prot-icons-blue) ; κίνηση (κινηματογράφος)
(frame "[]" prot-icons-gray)
(git "|-" prot-icons-gray)
(t ">." prot-icons-gray))
"Major modes or concepts and their corresponding icons.
Each element is a cons cell of the form (THING STRING FACE), where THING
is a symbol STRING is one or more characters that represent THING, and
FACE is the face to use for it, where applicable.")
(defvar prot-icons-emoji
'((dired-mode "📁" prot-icons-directory)
(archive-mode "💼" prot-icons-directory)
(diff-mode "🟰" prot-icons-yellow)
(prog-mode "🧬" prot-icons-magenta)
(conf-mode "📝" prot-icons-gray)
(text-mode "💬" prot-icons-green)
(comint-mode "🔮" prot-icons-gray)
(read-only "🔒" prot-icons-red)
(document "📜" prot-icons-red)
(audio "🔊" prot-icons-cyan)
(image "🎨" prot-icons-yellow)
(video "📹" prot-icons-blue)
(frame "🏠" prot-icons-gray)
(git "🪾" prot-icons-gray)
(t "🔸" prot-icons-gray))
"Same as `prot-icons-alphabetic' with emoji characters.")
(defvar prot-icons-symbolic
'((dired-mode "🖿" prot-icons-directory)
(archive-mode "🮯" prot-icons-directory)
(diff-mode "" prot-icons-yellow)
(prog-mode "" prot-icons-magenta)
(conf-mode "🗅" prot-icons-gray)
(text-mode "" prot-icons-green)
(comint-mode "" prot-icons-gray)
(read-only "" prot-icons-red)
(document "🗎" prot-icons-red)
(audio "𝅘𝅥𝅮" prot-icons-cyan)
(image "𜷻" prot-icons-yellow)
(video "" prot-icons-blue)
(frame "𜲋" prot-icons-gray)
(git "" prot-icons-gray)
(t "·" prot-icons-gray))
"Same as `prot-icons-alphabetic' with Unicode symbols.")
(defun prot-icons--get-style ()
"Return icons corresponding to `prot-icons-style'.
More specifically, return the value of `prot-icons-alphabetic' or
`prot-icons-emoji'."
(pcase prot-icons-style
('emoji prot-icons-emoji)
('symbolic prot-icons-symbolic)
(_ prot-icons-alphabetic)))
(defun prot-icons--get (thing)
"Return `prot-icons-alphabetic' representation of THING."
(unless (symbolp thing)
(error "The thing `%s' is not a symbol" thing))
(let ((icons (prot-icons--get-style)))
(when (string-suffix-p "-mode" (symbol-name thing))
(while-let ((parent (get thing 'derived-mode-parent)))
(setq thing parent)))
(or (alist-get thing icons)
(alist-get t icons))))
(defun prot-icons-get-icon (thing &optional face)
"Return propertized icon THING."
(pcase-let* ((`(,icon ,inherent-face) (prot-icons--get thing))
(face (or face inherent-face)))
(format "%2s" (propertize icon 'font-lock-face face 'face face))))
(defun prot-icons-get-file-icon (file)
"Return FILE icon and face."
(cond
((null file)
(prot-icons-get-icon nil))
((string-suffix-p "/" file)
(prot-icons-get-icon 'dired-mode))
((string-match-p (prot-common--get-file-type-regexp 'archive) file)
(prot-icons-get-icon 'archive-mode))
((string-match-p (prot-common--get-file-type-regexp 'text) file)
(prot-icons-get-icon 'text-mode))
((string-match-p (prot-common--get-file-type-regexp 'image) file)
(prot-icons-get-icon 'image))
((string-match-p (prot-common--get-file-type-regexp 'audio) file)
(prot-icons-get-icon 'audio))
((string-match-p (prot-common--get-file-type-regexp 'video) file)
(prot-icons-get-icon 'video))
((string-match-p (prot-common--get-file-type-regexp 'document) file)
(prot-icons-get-icon 'document))
((string-match-p (prot-common--get-file-type-regexp 'diff) file)
(prot-icons-get-icon 'diff-mode))
((string-match-p (prot-common--get-file-type-regexp 'program) file)
(prot-icons-get-icon 'prog-mode))
((string-match-p (prot-common--get-file-type-regexp 'program-data) file)
(prot-icons-get-icon 'conf-mode))
(t (prot-icons-get-icon nil))))
;;;; Icons for Dired
;; Adapted from `nerd-icons-dired'
(defun prot-icons-dired--add-overlay (pos string)
"Add overlay to display STRING at POS."
(let ((overlay (make-overlay (1- pos) pos)))
(overlay-put overlay 'prot-icons-overlay t)
(overlay-put overlay 'evaporate t)
(overlay-put overlay 'before-string (propertize string 'display string))
(overlay-put overlay 'after-string (propertize " " 'display '(space :align-to 1)))))
(defun prot-icons-dired--remove-all-overlays ()
"Remove all `prot-icons' overlays."
(dolist (buffer (buffer-list))
(when (and (derived-mode-p 'dired-mode) prot-icons-dired-mode)
(save-restriction
(widen)
(remove-overlays nil nil 'prot-icons-overlay t)))))
(defun prot-icons-dired--annotate ()
"Add icons to all files in the visible region of the buffer."
(save-excursion
(goto-char (point-min))
(while (and (dired-next-line 1) (not (eobp)))
(when-let* ((file (dired-get-filename nil :no-error))
(icon (if (file-directory-p file)
(prot-icons-get-file-icon (concat file "/"))
(prot-icons-get-file-icon file))))
(prot-icons-dired--add-overlay (dired-move-to-filename) icon)))))
(defun prot-icons-dired--refresh (&rest _)
"Update the display of icons of files in a Dired buffer."
(prot-icons-dired--remove-all-overlays)
(save-restriction
(widen)
(prot-icons-dired--annotate)))
(defun prot-icons-dired--setup ()
"Set up Dired to display icons."
(setq-local tab-width 1)
(prot-icons-dired--refresh))
;;;###autoload
(define-minor-mode prot-icons-dired-mode
"Display icons for Dired entries."
:global t
(if prot-icons-dired-mode
(progn
(add-hook 'dired-mode-hook #'prot-icons-dired--setup)
(add-hook 'dired-after-readin-hook 'prot-icons-dired--annotate)
(advice-add #'dired-do-redisplay :after #'prot-icons-dired--refresh)
(advice-add #'wdired-abort-changes :after #'prot-icons-dired--refresh))
(prot-icons-dired--remove-all-overlays)
(remove-hook 'dired-mode-hook #'prot-icons-dired--setup)
(remove-hook 'dired-after-readin-hook 'prot-icons-dired--annotate)
(advice-remove #'dired-do-redisplay #'prot-icons-dired--refresh)
(advice-remove #'wdired-abort-changes #'prot-icons-dired--refresh)))
;;;; Icons for Xref
;; Adapted from `nerd-icons-xref'
(defun prot-icons-xref--add-overlay (position string)
"Add overlay at POSITION to display STRING."
(let ((overlay (make-overlay position (+ position 1))))
(overlay-put overlay 'prot-icons-overlay t)
(overlay-put overlay 'evaporate t)
(overlay-put overlay 'before-string (format "%s " (propertize string 'display string)))))
(defun prot-icons-xref--add-icons ()
"Add icons to Xref headings."
(save-excursion
(goto-char (point-min))
(let ((prop))
(while (setq prop (text-property-search-forward 'xref-group))
(when-let* ((start (prop-match-beginning prop))
(end (prop-match-end prop))
(file (string-chop-newline (buffer-substring-no-properties start end)))
(icon (prot-icons-get-file-icon file)))
(prot-icons-xref--add-overlay start icon))))))
;;;###autoload
(define-minor-mode prot-icons-xref-mode
"Display icons for Xref headings."
:global t
(if prot-icons-xref-mode
(add-hook 'xref-after-update-hook #'prot-icons-xref--add-icons)
(remove-hook 'xref-after-update-hook #'prot-icons-xref--add-icons)))
;;;; Icons for Buffer menu
(defun prot-icons-buffer-menu--add-overlay (position string)
"Add overlay at POSITION to display STRING."
(let ((overlay (make-overlay position (+ position 1))))
(overlay-put overlay 'prot-icons-overlay t)
(overlay-put overlay 'evaporate t)
(overlay-put overlay 'before-string (propertize string 'display string))))
(defun prot-icons-buffer-menu--add-icons (&rest _)
"Add icons to `Buffer-menu-mode' entries."
(dolist (buffer (buffer-list))
(with-current-buffer buffer
(when (derived-mode-p 'Buffer-menu-mode)
(save-excursion
(goto-char (point-min))
(while-let ((match (text-property-search-forward 'tabulated-list-id))
(buffer (prop-match-value match))
(mode (with-current-buffer buffer major-mode))
(icon (prot-icons-get-icon mode)))
(prot-icons-buffer-menu--add-overlay (line-beginning-position) (concat icon " "))))))))
;;;###autoload
(define-minor-mode prot-icons-buffer-menu-mode
"Display icons for `Buffer-menu-mode' entries."
:global t
(if prot-icons-buffer-menu-mode
(advice-add #'list-buffers--refresh :after #'prot-icons-buffer-menu--add-icons)
(advice-remove #'list-buffers--refresh #'prot-icons-buffer-menu--add-icons)))
(provide 'prot-icons)
;;; prot-icons.el ends here

View file

@ -1,8 +1,8 @@
;;; prot-marginalia.el --- Code for my custom mode line -*- lexical-binding: t -*-
;; Copyright (C) 2023 Protesilaos Stavrou
;; Copyright (C) 2023-2026 Protesilaos
;; Author: Protesilaos Stavrou <info@protesilaos.com>
;; Author: Protesilaos <info@protesilaos.com>
;; URL: https://protesilaos.com/emacs/dotemacs
;; Version: 0.1.0
;; Package-Requires: ((emacs "30.1"))
@ -56,7 +56,7 @@
(defun prot-marginalia-buffer (buffer)
"Annotate BUFFER with the return value of function `buffer-file-name'."
(if-let ((name (buffer-file-name (get-buffer buffer))))
(if-let* ((name (buffer-file-name (get-buffer buffer))))
(prot-marginalia-display (abbreviate-file-name name))
(prot-marginalia-display (format "%s" (buffer-local-value 'major-mode (get-buffer buffer))))))
@ -66,7 +66,7 @@
(pkg (intern-soft package))
(desc (or (when (package-desc-p pkg) pkg)
(car (alist-get pkg pkg-alist))
(if-let (built-in (assq pkg package--builtins))
(if-let* ((built-in (assq pkg package--builtins)))
(package--from-builtin built-in)
(car (alist-get pkg package-archive-contents))))))
(prot-marginalia-display (package-desc-summary desc))))
@ -81,15 +81,15 @@
(defun prot-marginalia--first-line-documentation (symbol)
"Return first line of SYMBOL documentation string."
(when-let ((doc-string (prot-marginalia--get-symbol-doc symbol))
((stringp doc-string))
((not (string-empty-p doc-string))))
(when-let* ((doc-string (prot-marginalia--get-symbol-doc symbol))
((stringp doc-string))
((not (string-empty-p doc-string))))
(car (split-string doc-string "[?!.\n]"))))
(defun prot-marginalia-symbol (symbol)
"Annotate SYMBOL with its documentation string."
(when-let ((sym (intern-soft symbol))
(doc-string (prot-marginalia--first-line-documentation sym)))
(when-let* ((sym (intern-soft symbol))
(doc-string (prot-marginalia--first-line-documentation sym)))
(prot-marginalia-display doc-string)))
(provide 'prot-marginalia)

View file

@ -0,0 +1,411 @@
;;; prot-minibuffer.el --- Extensions for the minibuffer and completions -*- lexical-binding: t -*-
;; Copyright (C) 2025-2026 Protesilaos
;; Author: Protesilaos <info@protesilaos.com>
;; Maintainer: Protesilaos <info@protesilaos.com>
;; URL: https://protesilaos.com/emacs/dotemacs
;; Version: 0.1.0
;; Package-Requires: ((emacs "30.1"))
;; This file is NOT part of GNU Emacs.
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or (at
;; your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;;
;; Extensions for the minibuffer and completions, intended for my
;; Emacs setup: <https://protesilaos.com/emacs/dotemacs/>.
;;; Code:
(require 'prot-icons)
(defgroup prot-minibuffer nil
"Extensions for the minibuffer and completions."
:group 'minibuffer)
;;;; Completion category grouping, sorting, and affixating
;; Add some missing completion categories to let me configure the
;; relevant prompts via the `completion-category-overrides'.
(defun prot-minibuffer@read-from-kill-ring (&rest args)
(let ((completion-extra-properties (list :category 'prot-minibuffer-kill-ring)))
(apply args)))
(defun prot-minibuffer@read-library-name (&rest args)
(let ((completion-extra-properties (list :category 'prot-minibuffer-library)))
(apply args)))
(defun prot-minibuffer@emoji--read-emoji (&rest args)
(let ((completion-extra-properties (list :category 'prot-minibuffer-emoji)))
(apply args)))
(defun prot-minibuffer@password-store--completing-read (&rest args)
(let ((completion-extra-properties (list :category 'prot-minibuffer-pass)))
(apply args)))
(defun prot-minibuffer@read-input-method-name (&rest args)
(let ((completion-extra-properties (list :category 'prot-minibuffer-input-method)))
(apply args)))
(defun prot-minibuffer--do-advice (advice functions place)
"Handle the advice installed by the function `prot-minibuffer-missing-categories-mode'.
For each function in FUNCTIONS apply ARGS."
(dolist (original functions)
(when-let* ((my-function-name (format "prot-minibuffer@%s" original))
(my-function-symbol (intern-soft my-function-name))
(args (if place
(list advice original place my-function-symbol)
(list advice original my-function-symbol))))
(apply args))))
(defvar prot-minibuffer-missing-completion-categories-symbols
'( read-from-kill-ring read-library-name emoji--read-emoji
read-input-method-name password-store--completing-read)
"Symbols to be advised by `prot-minibuffer-missing-categories-mode'.")
;;;###autoload
(define-minor-mode prot-minibuffer-missing-categories-mode
"When enabled, add missing compleiton categories to relevant prompts."
:global t
(when-let* ((functions prot-minibuffer-missing-completion-categories-symbols))
(if prot-minibuffer-missing-categories-mode
(prot-minibuffer--do-advice #'advice-add functions :around)
(prot-minibuffer--do-advice #'advice-remove functions nil))))
(defun prot-minibuffer-file-sort (files)
"Sort FILES to have directories first and the rest alphabetically.
Omit the .. directory from FILES."
(setq files (delete "../" files))
(setq files (minibuffer-sort-alphabetically files))
(let ((directory-p (lambda (file) (string-suffix-p "/" file))))
(nconc (seq-filter directory-p files)
(seq-remove directory-p files))))
(defun prot-minibuffer-file-affixate (files)
"Return FILES with prefix and suffix."
(mapcar
(lambda (file)
(list file (format "%s " (prot-icons-get-file-icon file)) ""))
files))
(defun prot-minibuffer-file-group (file transform)
"Return FILE group name unless TRANSFORM is non-nil."
(cond
(transform file)
((string-suffix-p "/" file) "Directories")
((string-prefix-p "." file) "Dotfiles")
((when-let* ((extension (file-name-extension file :include-dot))
(_ (not (string-blank-p extension))))
extension))
(t "Other files")))
(defun prot-minibuffer--set-default-sort (candidates)
"Sort CANDIDATES according to `completions-sort' and return the sorted list."
(setq candidates
(pcase completions-sort
('nil candidates)
('alphabetical (minibuffer-sort-alphabetically candidates))
('historical (minibuffer-sort-by-history candidates))
(_ (funcall completions-sort candidates)))))
(defun prot-minibuffer-symbol-sort (symbols)
"Sort SYMBOLS so that public ones come first."
(setq symbols (prot-minibuffer--set-default-sort symbols))
(let ((private-p (lambda (symbol) (string-match-p "--" symbol))))
(nconc (seq-remove private-p symbols)
(seq-filter private-p symbols))))
(defun prot-minibuffer-symbol-group (symbol-name transform)
"Return SYMBOL-NAME group unless TRANSFORM is non-nil."
(let ((first-word-fn (lambda (string)
(if (string-match "\\(.*?\\)[@/-].*" string)
(match-string 1 string)
string))))
(cond
(transform symbol-name)
((funcall first-word-fn symbol-name)))))
(defun prot-minibuffer--propertize-suffix-with-space (string)
"Propertize STRING with spacing before it."
(format " %s%s"
(if (and (or (eq completions-format 'horizontal)
(eq completions-format 'vertical))
(not prot-emacs-completion-ui))
" "
(propertize " " 'display '(space :align-to 60)))
(propertize string 'face 'completions-annotations)))
(defun prot-minibuffer-buffer-sort (buffers)
"Sort BUFFERS by visibility.
This is a copy of `beframe-buffer-sort-visibility', from my `beframe'
package."
(let* ((bufs (seq-group-by
(lambda (buf)
(cond
((eq buf (current-buffer)) :current)
((get-buffer-window buf 'visible) :visible)
(t :hidden)))
buffers))
(hidden (alist-get :hidden bufs))
(visible (alist-get :visible bufs))
(current (alist-get :current bufs)))
(nconc hidden visible current)))
;; NOTE 2026-07-25: I am disabling this because it messes up with the sorting.
;; (defun prot-minibuffer-buffer-group (buffer-name transform)
;; "Return BUFFER-NAME group name unless TRANSFORM is non-nil."
;; (cond
;; (transform buffer-name)
;; ((string-prefix-p "*" buffer-name) "Special")
;; ((string-match-p "\\`magit.*?:" buffer-name) "Git")
;; ((if-let* ((buffer (get-buffer buffer-name)))
;; (with-current-buffer buffer
;; (cond
;; ((derived-mode-p 'dired-mode)
;; "Directory")
;; ((derived-mode-p 'prog-mode)
;; "Program")
;; ((derived-mode-p 'text-mode)
;; "Prose")
;; (t
;; (format "%s" major-mode))))
;; ""))))
(defun prot-minibuffer-buffer-affixate (buffers)
"Return BUFFERS with prefix and suffix."
(mapcar
(lambda (buffer)
(let* ((buffer-object (get-buffer buffer))
(mode (with-current-buffer buffer-object major-mode)))
(list
buffer
(format "%s " (prot-icons-get-icon mode))
(prot-minibuffer--propertize-suffix-with-space (format "%s" mode)))))
buffers))
(defun prot-minibuffer-bookmark-affixate (bookmarks)
"Return BOOKMARKS with prefix and suffix."
(mapcar
(lambda (bookmark)
(let* ((data (bookmark-get-bookmark bookmark))
(file (bookmark-prop-get data 'filename)))
(list
bookmark
(format "%s " (prot-icons-get-file-icon file))
(prot-minibuffer--propertize-suffix-with-space (format "%s" file)))))
bookmarks))
(defun prot-minibuffer-library-sort (libraries)
"Sort LIBRARIES, omitting autoloads and bytecode files."
(setq libraries (seq-remove
(lambda (library)
(string-match-p "\\(-autoload\\|\\.elc\\|\\.dir-locals\\)" library))
libraries))
(prot-minibuffer--set-default-sort libraries))
(defcustom prot-minibuffer-used-input-methods
'("greek" "french-postfix" "spanish-postfix")
"Input methods that I use."
:type '(choice (const nil)
(repeat mule-input-method-string)))
(defun prot-minibuffer--my-method-p (input-method)
"Return non-nil if INPUT-METHOD is a member of `prot-minibuffer-used-input-methods'."
(member input-method prot-minibuffer-used-input-methods))
(defun prot-minibuffer-input-method-sort (input-methods)
"Sort INPUT-METHODS into `prot-minibuffer-used-input-methods' and the rest."
(setq input-methods (prot-minibuffer--set-default-sort input-methods))
(nconc (seq-filter #'prot-minibuffer--my-method-p input-methods)
(seq-remove #'prot-minibuffer--my-method-p input-methods)))
(defun prot-minibuffer-input-method-group (input-method transform)
"Group INPUT-METHOD into `prot-minibuffer-used-input-methods' and the rest.
Do it when TRANSFORM is non-nil, else return INPUT-METHOD."
(cond
(transform input-method)
((prot-minibuffer--my-method-p input-method) "My input methods")
(t "Other input methods")))
;; NOTE 2025-12-19: Maybe there is a better way, but this is okay to start with.
(defun prot-minibuffer-library-annotate (library)
"Return the group documentation of LIBRARY."
(when-let* ((group-documentation (get (intern-soft library) 'group-documentation)))
(prot-minibuffer--propertize-suffix-with-space group-documentation)))
(defun prot-minibuffer-command-annotate (command)
"Annotate COMMAND with its key binding and shortened documentation string."
(let* ((symbol (intern-soft command))
(key (if-let* ((binding (where-is-internal symbol overriding-local-map t))
(description (key-description binding))
(_ (and binding (not (stringp binding)))))
(format " %s " (propertize description 'face 'help-key-binding))
""))
(doc (if-let* ((doc (condition-case nil
(documentation symbol)
(error nil)))
(first-line (substring doc 0 (string-search "\n" doc))))
(propertize first-line 'face 'completions-annotations)
"")))
(format "%s%s" key (prot-minibuffer--propertize-suffix-with-space doc))))
;;;; Completions
(defun prot-minibuffer-completions-tweak-style ()
"Tweak the style of the Completions buffer."
(setq-local mode-line-format nil)
(setq-local cursor-in-non-selected-windows nil)
(when (and completions-header-format
(not (string-blank-p completions-header-format)))
(setq-local display-line-numbers-offset -1))
(display-line-numbers-mode 1))
(defun prot-minibuffer-quit-completions ()
"Always quit the Completions window."
(when-let* ((window (get-buffer-window "*Completions*")))
(quit-window nil window)))
(defun prot-minibuffer-choose-completion-no-exit ()
"Call `choose-completion' without exiting the minibuffer.
Also see `prot-minibuffer-choose-completion-exit' and
`prot-minibuffer-choose-completion-dwim'."
(interactive)
(if minibuffer-visible-completions
(minibuffer-choose-completion :no-exit :no-quit)
(choose-completion nil :no-exit :no-quit)
(switch-to-minibuffer)))
(defun prot-minibuffer-choose-completion-exit ()
"Call `choose-completion' and exit the minibuffer.
Also see `prot-minibuffer-choose-completion-no-exit' and
`prot-minibuffer-choose-completion-dwim'."
(interactive)
(if minibuffer-visible-completions
(or (minibuffer-choose-completion-or-exit) (exit-minibuffer))
(choose-completion nil :no-exit)
(exit-minibuffer)))
(defun prot-minibuffer-crm-p ()
"Return non-nil if `completing-read-multiple' is in use."
(when-let* ((_ (featurep 'crm))
(window (active-minibuffer-window))
(buffer (window-buffer window)))
(buffer-local-value 'crm-completion-table buffer)))
(defun prot-minibuffer-empty-p ()
"Return non-nil if the minibuffer is empty."
(eq (minibuffer-prompt-end) (point-max)))
(defun prot-minibuffer-choose-completion-dwim ()
"Call `choose-completion' that exits only on a unique match.
If the match is not unique, then complete up to the largest common
prefix or, anyhow, continue with the completion (e.g. in `find-file'
switch into the directory and then show the files therein).
Also see `prot-minibuffer-choose-completion-no-exit' and
`prot-minibuffer-choose-completion-exit'."
(interactive)
(if (prot-minibuffer-crm-p)
(if (prot-minibuffer-empty-p)
(prot-minibuffer-choose-completion-no-exit)
(prot-minibuffer-choose-completion-exit))
(choose-completion nil :no-exit :no-quit)
(switch-to-minibuffer)
(minibuffer-completion-help)
(unless (get-buffer-window "*Completions*")
(exit-minibuffer))))
(define-advice minibuffer-completion-help (:around (&rest args) prot)
"Make `minibuffer-completion-help' display *Completions* in a side window.
Make the window be at slot 0, such that the *Help* buffer produced by
`prot-minibuffer-completions-describe-at-point' is to its right."
(let ((display-buffer-overriding-action
`((display-buffer-reuse-mode-window display-buffer-in-side-window)
(mode . completion-list-mode)
(side . bottom)
(slot . 0))))
(apply args)))
(defun prot-minibuffer-completions-describe-at-point (symbol)
"Describe SYMBOL at point inside the *Completions* buffer.
Place the *Help* buffer in a side window, situated to the right of the
*Completions* buffer. Make the window have the `prot-minibuffer-help'
property, such that it can be found by `prot-minibuffer-completions-close-help'."
(interactive
(list
(intern-soft
(if minibuffer-visible-completions
(completion--selected-candidate)
(thing-at-point 'symbol)))))
(when symbol
(let ((help-window-select nil)
(display-buffer-overriding-action
`((display-buffer-reuse-mode-window display-buffer-in-side-window)
(mode . help-mode)
(side . bottom)
(slot . 1)
(window-height . fit-window-to-buffer)
(window-parameters . ((prot-minibuffer-help . t))))))
(describe-symbol symbol))))
(defun prot-minibuffer-completions-close-help ()
"Close the window that has a `prot-minibuffer-help' parameter."
(when-let* ((help (seq-find
(lambda (window)
(window-parameter window 'prot-minibuffer-help))
(window-list))))
(delete-window help)))
;;;###autoload
(define-minor-mode prot-minibuffer-completions-mode
"Tweak the interface of the minibuffer and the *Completions*."
:global t
(if prot-minibuffer-completions-mode
(progn
(setq completion-show-help nil)
(setq completion-show-inline-help nil)
(setq completions-detailed t)
(setq completions-format 'one-column)
(setq completions-header-format "")
(setq completions-max-height 12)
(setq completions-sort 'historical)
(setq completion-auto-help t)
(setq completion-auto-select nil)
(setq minibuffer-visible-completions t)
(setq completion-eager-display 'auto)
(setq completion-eager-update 'auto)
(add-hook 'completion-list-mode-hook #'prot-minibuffer-completions-tweak-style)
(add-hook 'minibuffer-exit-hook #'prot-minibuffer-quit-completions)
(add-hook 'minibuffer-exit-hook #'prot-minibuffer-completions-close-help))
(setq completion-show-help t)
(setq completion-show-inline-help nil)
(setq completions-detailed nil)
(setq completions-format 'horizontal)
(setq completions-header-format (propertize "%s possible completions:\n" 'face 'shadow))
(setq completions-max-height nil)
(setq completions-sort 'alphabetical)
(setq completion-auto-help t)
(setq completion-auto-select nil)
(setq minibuffer-visible-completions nil)
(setq completion-eager-display 'auto)
(setq completion-eager-update 'auto)
(remove-hook 'completion-list-mode-hook #'prot-minibuffer-completions-tweak-style)
(remove-hook 'minibuffer-exit-hook #'prot-minibuffer-quit-completions)
(remove-hook 'minibuffer-exit-hook #'prot-minibuffer-completions-close-help)))
(provide 'prot-minibuffer)
;;; prot-minibuffer.el ends here

View file

@ -1,8 +1,8 @@
;;; prot-modeline.el --- Code for my custom mode line -*- lexical-binding: t -*-
;; Copyright (C) 2023 Protesilaos Stavrou
;; Copyright (C) 2023-2026 Protesilaos
;; Author: Protesilaos Stavrou <info@protesilaos.com>
;; Author: Protesilaos <info@protesilaos.com>
;; URL: https://protesilaos.com/emacs/dotemacs
;; Version: 0.1.0
;; Package-Requires: ((emacs "30.1"))
@ -23,15 +23,11 @@
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;;
;; Remember that every piece of Elisp that I write is for my own
;; educational and recreational purposes. I am not a programmer and I
;; do not recommend that you copy any of this if you are not certain of
;; what it does.
;;; Code:
(require 'prot-common)
(require 'prot-icons)
(defgroup prot-modeline nil
"Custom modeline that is stylistically close to the default."
@ -53,6 +49,11 @@ Modify this face to, for example, add a :box attribute to all
relevant indicators (combines nicely with my `spacious-padding'
package).")
(defface prot-modeline-indicator-small
'((t :height 0.8))
"Face for modeline indicators (e.g. see my `notmuch-indicator')."
:group 'prot-modeline-faces)
(defface prot-modeline-indicator-red
'((default :inherit bold)
(((class color) (min-colors 88) (background light))
@ -174,7 +175,7 @@ package).")
:group 'prot-modeline-faces)
(defface prot-modeline-indicator-gray
'((t :inherit shadow))
'((t :inherit (bold shadow)))
"Face for modeline indicators (e.g. see my `notmuch-indicator')."
:group 'prot-modeline-faces)
@ -192,11 +193,14 @@ package).")
(defun prot-modeline--string-truncate-p (str)
"Return non-nil if STR should be truncated."
(if (string-empty-p str)
str
(and (prot-common-window-narrow-p)
(cond
((or (not (stringp str))
(string-empty-p str)
(string-blank-p str))
nil)
((and (prot-common-window-narrow-p)
(> (length str) prot-modeline-string-truncate-length)
(not (one-window-p :no-minibuffer)))))
(not (one-window-p :no-minibuffer))))))
(defun prot-modeline--truncate-p ()
"Return non-nil if truncation should happen.
@ -271,15 +275,16 @@ Specific to the current window's mode line.")
'(:eval
(when (and (mode-line-window-selected-p)
(buffer-narrowed-p)
(not (derived-mode-p 'Info-mode 'help-mode 'special-mode 'message-mode)))
(not (memq major-mode '(Info-mode help-mode message-mode special-mode))))
(propertize " Narrow " 'face 'prot-modeline-indicator-cyan-bg)))
"Mode line construct to report the multilingual environment.")
"Mode line construct to report the narrowed state of the current buffer.")
;;;; Input method
(defvar-local prot-modeline-input-method
'(:eval
(when current-input-method-title
(when (and (mode-line-window-selected-p)
current-input-method-title)
(propertize (format " %s " current-input-method-title)
'face 'prot-modeline-indicator-green-bg
'mouse-face 'mode-line-highlight)))
@ -325,16 +330,9 @@ Specific to the current window's mode line.")
(defun prot-modeline--buffer-name ()
"Return `buffer-name', truncating it if necessary.
See `prot-modeline-string-cut-middle'."
(when-let ((name (buffer-name)))
(when-let* ((name (buffer-name)))
(prot-modeline-string-cut-middle name)))
(defun prot-modeline-buffer-name ()
"Return buffer name, with read-only indicator if relevant."
(let ((name (prot-modeline--buffer-name)))
(if buffer-read-only
(format "%s %s" (char-to-string #xE0A2) name)
name)))
(defun prot-modeline-buffer-name-help-echo ()
"Return `help-echo' value for `prot-modeline-buffer-identification'."
(concat
@ -346,25 +344,27 @@ See `prot-modeline-string-cut-middle'."
'face 'font-lock-doc-face)))
(defvar-local prot-modeline-buffer-identification
'(:eval
(propertize (prot-modeline-buffer-name)
'face (prot-modeline-buffer-identification-face)
'mouse-face 'mode-line-highlight
'help-echo (prot-modeline-buffer-name-help-echo)))
'(:eval
(let* ((name (propertize (prot-modeline--buffer-name)
'face (prot-modeline-buffer-identification-face)
'mouse-face 'mode-line-highlight
'help-echo (prot-modeline-buffer-name-help-echo)))
(read-only-icon (prot-icons-get-icon
'read-only
(unless (mode-line-window-selected-p)
'prot-modeline-indicator-gray))))
(if buffer-read-only
(format "%s %s" read-only-icon name)
name)))
"Mode line construct for identifying the buffer being displayed.
Propertize the current buffer with the `mode-line-buffer-id'
face. Let other buffers have no face.")
;;;; Major mode
(defun prot-modeline-major-mode-indicator ()
"Return appropriate propertized mode line indicator for the major mode."
(let ((indicator (cond
((derived-mode-p 'text-mode) "§")
((derived-mode-p 'prog-mode) "λ")
((derived-mode-p 'comint-mode) ">_")
(t ""))))
(propertize indicator 'face 'shadow)))
(defun prot-modeline-major-mode-icon ()
"Return icon for the major mode."
(prot-icons-get-icon major-mode 'prot-modeline-indicator-gray))
(defun prot-modeline-major-mode-name ()
"Return capitalized `major-mode' without the -mode suffix."
@ -372,7 +372,7 @@ face. Let other buffers have no face.")
(defun prot-modeline-major-mode-help-echo ()
"Return `help-echo' value for `prot-modeline-major-mode'."
(if-let ((parent (get major-mode 'derived-mode-parent)))
(if-let* ((parent (get major-mode 'derived-mode-parent)))
(format "Symbol: `%s'. Derived from: `%s'" major-mode parent)
(format "Symbol: `%s'." major-mode)))
@ -381,7 +381,7 @@ face. Let other buffers have no face.")
(propertize "%[" 'face 'prot-modeline-indicator-red)
'(:eval
(concat
(prot-modeline-major-mode-indicator)
(prot-modeline-major-mode-icon)
" "
(propertize
(prot-modeline-string-abbreviate-but-last
@ -402,31 +402,11 @@ face. Let other buffers have no face.")
(defun prot-modeline--vc-branch-name (file backend)
"Return capitalized VC branch name for FILE with BACKEND."
(when-let ((rev (vc-working-revision file backend))
(branch (or (vc-git--symbolic-ref file)
(substring rev 0 7))))
(when-let* ((rev (vc-working-revision file backend))
(branch (or (vc-git--symbolic-ref file)
(substring rev 0 7))))
(capitalize branch)))
;; NOTE 2023-07-27: This is a good idea, but it hardcodes Git, whereas
;; I want a generic VC method. Granted, I only use Git but I still
;; want it to work as a VC extension.
;; (defun prot-modeline-diffstat (file)
;; "Return shortened Git diff numstat for FILE."
;; (when-let* ((output (shell-command-to-string (format "git diff --numstat %s" file)))
;; (stats (split-string output "[\s\t]" :omit-nulls "[\s\f\t\n\r\v]+"))
;; (added (nth 0 stats))
;; (deleted (nth 1 stats)))
;; (cond
;; ((and (equal added "0") (equal deleted "0"))
;; "")
;; ((and (not (equal added "0")) (equal deleted "0"))
;; (propertize (format "+%s" added) 'face 'shadow))
;; ((and (equal added "0") (not (equal deleted "0")))
;; (propertize (format "-%s" deleted) 'face 'shadow))
;; (t
;; (propertize (format "+%s -%s" added deleted) 'face 'shadow)))))
(declare-function vc-git-working-revision "vc-git" (file))
(defvar prot-modeline-vc-map
@ -434,7 +414,7 @@ face. Let other buffers have no face.")
(define-key map [mode-line down-mouse-1] 'vc-diff)
(define-key map [mode-line down-mouse-3] 'vc-root-diff)
map)
"Keymap to display on VC indicator.")
"Keymap for the `prot-modeline-vc-branch'.")
(defun prot-modeline--vc-help-echo (file)
"Return `help-echo' message for FILE tracked by VC."
@ -444,17 +424,14 @@ face. Let other buffers have no face.")
(defun prot-modeline--vc-text (file branch &optional face)
"Prepare text for Git controlled FILE, given BRANCH.
With optional FACE, use it to propertize the BRANCH."
(concat
(propertize (char-to-string #xE0A0) 'face 'shadow)
" "
(propertize branch
'face face
'mouse-face 'mode-line-highlight
'help-echo (prot-modeline--vc-help-echo file)
'local-map prot-modeline-vc-map)
;; " "
;; (prot-modeline-diffstat file)
))
(format "%s %s "
(prot-icons-get-icon 'git 'prot-modeline-indicator-gray)
;; (propertize (char-to-string #xE0A0) 'face 'prot-modeline-indicator-gray)
(propertize branch
'face face
'mouse-face 'mode-line-highlight
'help-echo (prot-modeline--vc-help-echo file)
'local-map prot-modeline-vc-map)))
(defun prot-modeline--vc-details (file branch &optional face)
"Return Git BRANCH details for FILE, truncating it if necessary.
@ -475,17 +452,18 @@ than `split-width-threshold'."
(defun prot-modeline--vc-get-face (key)
"Get face from KEY in `prot-modeline--vc-faces'."
(alist-get key prot-modeline--vc-faces 'up-to-date))
(alist-get key prot-modeline--vc-faces 'vc-up-to-date-state))
(defun prot-modeline--vc-face (file backend)
"Return VC state face for FILE with BACKEND."
(prot-modeline--vc-get-face (vc-state file backend)))
(when-let* ((key (vc-state file backend)))
(prot-modeline--vc-get-face key)))
(defvar-local prot-modeline-vc-branch
'(:eval
(when-let* (((mode-line-window-selected-p))
(file (buffer-file-name))
(backend (vc-backend file))
(file (or buffer-file-name default-directory))
(backend (or (vc-backend file) 'Git))
;; ((vc-git-registered file))
(branch (prot-modeline--vc-branch-name file backend))
(face (prot-modeline--vc-face file backend)))
@ -519,18 +497,18 @@ TYPE is usually keyword `:error', `:warning' or `:note'."
(defmacro prot-modeline-flymake-type (type indicator &optional face)
"Return function that handles Flymake TYPE with stylistic INDICATOR and FACE."
`(defun ,(intern (format "prot-modeline-flymake-%s" type)) ()
(when-let ((count (prot-modeline-flymake-counter
,(intern (format ":%s" type)))))
(when-let* ((count (prot-modeline-flymake-counter
,(intern (format ":%s" type)))))
(concat
(propertize ,indicator 'face 'shadow)
(propertize ,indicator 'face 'prot-modeline-indicator-gray)
(propertize count
'face ',(or face type)
'mouse-face 'mode-line-highlight
;; FIXME 2023-07-03: Clicking on the text with
;; this buffer and a single warning present, the
;; diagnostics take up the entire frame. Why?
'local-map prot-modeline-flymake-map
'help-echo "mouse-1: buffer diagnostics\nmouse-3: project diagnostics")))))
'mouse-face 'mode-line-highlight
;; FIXME 2023-07-03: Clicking on the text with
;; this buffer and a single warning present, the
;; diagnostics take up the entire frame. Why?
'local-map prot-modeline-flymake-map
'help-echo "mouse-1: buffer diagnostics\nmouse-3: project diagnostics")))))
(prot-modeline-flymake-type error "")
(prot-modeline-flymake-type warning "!")
@ -561,6 +539,52 @@ Specific to the current window's mode line.")
"Mode line construct displaying Eglot information.
Specific to the current window's mode line.")
;;;; Frame name
(defcustom prot-modeline-show-frame-name nil
"When non-nil, display the current frame name."
:type 'boolean)
(defvar prot-modeline-frame-name-map
(let ((map (make-sparse-keymap)))
(define-key map [mode-line down-mouse-1] 'buffer-menu)
map)
"Keymap for the `prot-modeline-frame-name'.")
(defvar-local prot-modeline-frame-name
'(prot-modeline-show-frame-name
(" "
(:eval
(when-let* ((_ (mode-line-window-selected-p))
(current-frame (selected-frame))
(_ (frame-live-p current-frame))
(parameters (frame-parameters))
(name (capitalize (alist-get 'name parameters))))
(format "%s %s "
(prot-icons-get-icon 'frame 'prot-modeline-indicator-gray)
(propertize name
'mouse-face 'mode-line-highlight
'help-echo (format "Current frame name\nmouse-1: `buffer-menu'")
'local-map prot-modeline-frame-name-map))))))
"Mode line construct to display the current frame name.")
;;;; `which-function-mode' indicator
(defvar-local prot-modeline-which-function-indicator
'(which-function-mode
(:eval
(when (or (provided-mode-derived-p major-mode 'text-mode)
(provided-mode-derived-p major-mode 'prog-mode))
(let ((string (or (gethash (selected-window) which-func-table) which-func-unknown)))
(propertize string
'face '(prot-modeline-indicator-small which-func)
'mouse-face 'mode-line-highlight
'help-echo (format "Current definition: `%s'" string))))))
"The equivalent of `which-func-format'.")
(with-eval-after-load 'which-func
(setq mode-line-misc-info (delete (assq 'which-function-mode mode-line-misc-info) mode-line-misc-info)))
;;;; Miscellaneous
(defvar-local prot-modeline-notmuch-indicator
@ -593,7 +617,8 @@ Specific to the current window's mode line.")
prot-modeline-vc-branch
prot-modeline-flymake
prot-modeline-eglot
;; prot-modeline-align-right
prot-modeline-frame-name
prot-modeline-which-function-indicator
prot-modeline-notmuch-indicator
prot-modeline-misc-info))
(put construct 'risky-local-variable t))

View file

@ -1,8 +1,8 @@
;;; prot-notmuch.el --- Tweaks for my notmuch.el configurations -*- lexical-binding: t -*-
;; Copyright (C) 2021-2023 Protesilaos Stavrou
;; Copyright (C) 2021-2026 Protesilaos
;; Author: Protesilaos Stavrou <info@protesilaos.com>
;; Author: Protesilaos <info@protesilaos.com>
;; URL: https://protesilaos.com/emacs/dotemacs
;; Version: 0.1.0
;; Package-Requires: ((emacs "30.1"))
@ -26,11 +26,6 @@
;;
;; This covers my tweaks for notmuch.el that are meant for use in my
;; Emacs setup: https://protesilaos.com/emacs/dotemacs.
;;
;; Remember that every piece of Elisp that I write is for my own
;; educational and recreational purposes. I am not a programmer and I
;; do not recommend that you copy any of this if you are not certain of
;; what it does.
;;; Code:

View file

@ -1,8 +1,8 @@
;;; prot-orderless.el --- Extensions for Orderless -*- lexical-binding: t -*-
;; Copyright (C) 2020-2023 Protesilaos Stavrou
;; Copyright (C) 2020-2026 Protesilaos
;; Author: Protesilaos Stavrou <info@protesilaos.com>
;; Author: Protesilaos <info@protesilaos.com>
;; URL: https://protesilaos.com/emacs/dotemacs
;; Version: 0.1.0
;; Package-Requires: ((emacs "30.1"))
@ -52,8 +52,8 @@
(defun prot-orderless-beg-or-end (word _index _total)
"Expand WORD~ to \\(^WORD\\|WORD$\\)."
(when-let (((string-suffix-p "~" word))
(word (substring word 0 -1)))
(when-let* (((string-suffix-p "~" word))
(word (substring word 0 -1)))
`(orderless-regexp . ,(format "\\(^%s\\|%s$\\)" word word))))
(provide 'prot-orderless)

View file

@ -1,8 +1,8 @@
;;; prot-org.el --- Tweaks for my org-mode configurations -*- lexical-binding: t -*-
;; Copyright (C) 2021-2023 Protesilaos Stavrou
;; Copyright (C) 2021-2026 Protesilaos
;; Author: Protesilaos Stavrou <info@protesilaos.com>
;; Author: Protesilaos <info@protesilaos.com>
;; URL: https://protesilaos.com/emacs/dotemacs
;; Version: 0.1.0
;; Package-Requires: ((emacs "30.1"))
@ -26,11 +26,6 @@
;;
;; This covers my tweaks for Org that are meant for use in my
;; Emacs setup: https://protesilaos.com/emacs/dotemacs.
;;
;; Remember that every piece of Elisp that I write is for my own
;; educational and recreational purposes. I am not a programmer and I
;; do not recommend that you copy any of this if you are not certain of
;; what it does.
;;; Code:
@ -43,83 +38,6 @@
;;;; org-capture
(defvar prot-org--capture-coach-person-history nil)
(declare-function message-fetch-field "message" (header &optional first))
(declare-function notmuch-show-get-header "notmuch-show")
(defun prot-org--capture-coach-person-message-from ()
"Return default value for `prot-org--capture-coach-person-prompt'."
(when-let ((from (cond
((derived-mode-p 'message-mode)
(message-fetch-field "To"))
((derived-mode-p 'notmuch-show-mode)
(notmuch-show-get-header :From)))))
(string-clean-whitespace (car (split-string from "<")))))
(defun prot-org--capture-coach-person-message-from-and-subject ()
"Return default value for `prot-org--capture-coach-person-prompt'."
(cond
((derived-mode-p 'message-mode)
(message-fetch-field "Subject"))
((derived-mode-p 'notmuch-show-mode)
(notmuch-show-get-header :Subject))))
(defun prot-org--capture-coach-person-prompt ()
"Prompt for person for use in `prot-org-capture-coach'."
(completing-read "Person to coach: "
prot-org--capture-coach-person-history
nil nil nil
'prot-org--capture-coach-person-history
(prot-org--capture-coach-person-message-from)))
(defvar prot-org--capture-coach-description-history nil)
(defun prot-org--capture-coach-description-prompt ()
"Prompt for description in `prot-org-capture-coach'."
(read-string "Description: "
nil
'prot-org--capture-coach-description-history
(prot-org--capture-coach-person-message-from-and-subject)))
(defun prot-org--capture-coach-date-prompt-range ()
"Prompt for Org date and return it as a +1h range.
For use in `prot-org-capture-coach'."
(let ((date (org-read-date :with-time)))
;; We cannot use this here, unfortunately, as the Org agenda
;; interprets it both as a deadline and an event with the date
;; range.
;;
;; (format "DEADLINE: <%s>--<%s>\n" date
(format "<%s>--<%s>\n" date
(org-read-date
:with-time nil "++1h" nil
(org-encode-time (org-parse-time-string date))))))
(defun prot-org-capture-coach ()
"Contents of an Org capture template for my coaching lessons."
(concat "* COACH " (prot-org--capture-coach-person-prompt) " "
(prot-org--capture-coach-description-prompt) " :lesson:\n"
;; See comment above
;; (prot-org--capture-coach-date-prompt-range)
"DEADLINE: %^T\n"
":PROPERTIES:\n"
":CAPTURED: %U\n"
":APPT_WARNTIME: 20\n"
":END:\n\n"
"%a%?"))
(defun prot-org-capture-coach-clock ()
"Contents of an Org capture for my clocked coaching services."
(concat "* COACH " (prot-org--capture-coach-person-prompt) " "
(prot-org--capture-coach-description-prompt) " :service:\n"
;; See comment above
;; (prot-org--capture-coach-date-prompt-range)
":PROPERTIES:\n"
":CAPTURED: %U\n"
":END:\n\n"
"%a%?"))
(declare-function cl-letf "cl-lib")
;; Adapted from source: <https://stackoverflow.com/a/54251825>.
@ -129,6 +47,7 @@ For use in `prot-org-capture-coach'."
;; we were missing the `&rest':
;; <https://github.com/org-roam/org-roam/issues/2142#issuecomment-1100718373>.
(defun prot-org--capture-no-delete-windows (&rest args)
"Apply ARGS while ignoring `delete-other-windows'."
(cl-letf (((symbol-function 'delete-other-windows) 'ignore))
(apply args)))
@ -136,6 +55,228 @@ For use in `prot-org-capture-coach'."
(advice-add 'org-capture-place-template :around 'prot-org--capture-no-delete-windows)
(advice-add 'org-add-log-note :around 'prot-org--capture-no-delete-windows)
;;;;; Custom function to select a project to add to
(defun prot-org--get-outline (&optional file)
"Return `outline-regexp' headings and line numbers of current file or FILE."
(with-current-buffer (find-file-noselect file)
(let ((outline-regexp (format "^\\(?:%s\\)" (or (bound-and-true-p outline-regexp) "[*\^L]+")))
candidates)
(save-excursion
(goto-char (point-min))
(while (if (bound-and-true-p outline-search-function)
(funcall outline-search-function)
(re-search-forward outline-regexp nil t))
(push
;; NOTE 2024-11-24: The -5 (minimum width) is a sufficiently high number to keep the
;; alignment consistent in most cases. Larger files will simply shift the heading text
;; in minibuffer, but this is not an issue anymore.
(format "%-5s\t%s"
(line-number-at-pos (point))
(buffer-substring-no-properties (line-beginning-position) (line-end-position)))
candidates)
(goto-char (1+ (line-end-position)))))
(if candidates
(nreverse candidates)
(user-error "No outline")))))
(defvar prot-org-outline-history nil
"Minibuffer history for `prot-org-outline-prompt'.")
(defun prot-org-outline-prompt (&optional file)
"Prompt for outline among headings retrieved by `prot-org--get-outline'.
With optional FILE use the outline of it, otherwise use that of
the current file."
(let ((current-file (or file buffer-file-name))
(default (car prot-org-outline-history)))
(completing-read
(format-prompt
(format "Select heading inside `%s': "
(propertize (file-name-nondirectory current-file) 'face 'error))
default)
(prot-common-completion-table-no-sort 'imenu (prot-org--get-outline current-file))
nil :require-match nil 'prot-org-outline-history default)))
(defvar prot-org-file-history nil
"Minibuffer history of `prot-org-file-prompt'.")
(defun prot-org--not-useful-p (file)
"Return non-nil if FILE is not a useful Org file for `org-capture'."
(or (string-match-p "\\.org_archive\\'" file)
(backup-file-name-p file)
(not (string-match-p "\\.org\\'" file))))
(defun prot-org-file-prompt ()
"Select a file in the `org-directory'."
(if-let* ((dir org-directory)
(files (directory-files-recursively org-directory ".*" nil))
(files (seq-remove #'prot-org--not-useful-p files)))
(let ((default (car prot-org-file-history)))
(completing-read
(format-prompt "Select file" default)
(prot-common-completion-table 'file files)
nil :require-match nil 'prot-org-file-history default))
(user-error "There are no files in the `org-directory'")))
;;;###autoload
(defun prot-org-select-heading-in-file ()
"Move to heading in Org file stored in `org-directory'.
Prompt for file, then for heading inside of that file.
This function can also be used as part of an `org-capture' template to
navigate to a file+heading and then capture something which is inserted
there."
(declare (interactive-only t))
(interactive)
(pcase-let* ((file (prot-org-file-prompt))
(line-with-heading (prot-org-outline-prompt file))
(`(,line ,text) (split-string line-with-heading "\t"))
(line (string-to-number line)))
;; NOTE 2024-11-24: `with-current-buffer' does not work with `org-capture'.
(find-file file)
(goto-char (point-min))
(forward-line (1- line))))
(defalias 'prot-org-goto-heading-in-file 'prot-org-select-heading-in-file
"Alias for `prot-org-select-heading-in-file'.")
;;;; Org clock report
(defvar prot-org-clock--template-with-effort
"#+BEGIN: clocktable :formula % :properties (\"Effort\") :timestamp t :sort (1 . ?a) :link t :scope nil :hidefiles t :maxlevel 8 :stepskip0 t
#+END:"
"Clock table with effort estimate column to use for custom clock reports.")
(defvar prot-org-clock--template-no-effort
"#+BEGIN: clocktable :formula % :timestamp t :sort (1 . ?a) :link nil :scope nil :hidefiles t :maxlevel 8 :stepskip0 t
#+END:"
"Clock table to use for custom clock reports.")
(defvar prot-org-clock--ranges
'( today yesterday thisweek lastweek thismonth
lastmonth thisyear lastyear untilnow)
"Time ranges of my interest for clock reports.")
(defvar prot-org-clock--report-range-history nil
"Minibuffer history for `prot-org-clock--report-range-prompt'.")
(defun prot-org-clock--report-range-prompt ()
"Prompt for a clock table range among `prot-org-clock--ranges'."
(let ((default (car prot-org-clock--report-range-history)))
(completing-read
(format-prompt "Select a time range for the clock" default)
prot-org-clock--ranges nil :require-match nil 'prot-org-clock--report-range-history
default)))
(defun prot-org-clock--get-report (scope)
"Produce clock report with current file SCOPE and return its buffer.
SCOPE is a symbol of either `file' or `subtree'. If the former, then
use the entire file's contents. Else use those of the current subtree."
(let ((buffer (get-buffer-create "*prot-org-custom-clock-report*")))
(save-restriction
(unwind-protect
(progn
(pcase scope
('file nil)
('subtree (org-narrow-to-subtree))
(_ (error "The scope `%s' is unknown" scope)))
(let ((contents (buffer-substring (point-min) (point-max))))
(with-current-buffer buffer
(erase-buffer)
(org-mode)
(save-excursion
(insert (format "%s\n\n" prot-org-clock--template-with-effort))
(insert contents))
(save-excursion
(let ((range (prot-org-clock--report-range-prompt)))
(goto-char (line-end-position))
(insert (concat " :block " range))))
(org-dblock-update))))
(widen)))
buffer))
;;;###autoload
(defun prot-org-clock-report-current-subtree-or-file (&optional whole-buffer)
"Produce a clock report in a new buffer for the subtree at point.
With optional WHOLE-BUFFER as a non-nil value, operate on the entire file.
When called interactively WHOLE-BUFFER is a prefix argument."
(interactive "P")
(when-let* ((buffer (prot-org-clock--get-report (if whole-buffer 'file 'subtree))))
(pop-to-buffer buffer)))
;;;###autoload
(defun prot-org-clock-select-heading-and-clock-report ()
"Select a heading in a file and do a clock report for it in a new buffer."
(interactive)
(call-interactively 'prot-org-select-heading-in-file)
(call-interactively 'prot-org-clock-report-current-subtree-or-file))
;;;;; Coaching-related Org custom clocking
;; TODO 2024-12-15: This sort of thing must exist in Org, but I did
;; not find it.
(defun prot-org--timestamp-to-time (string)
"Return time object of STRING timestamp."
(org-timestamp-to-time (org-timestamp-from-string string)))
(defun prot-org-coach--get-entries (todo-keyword string since)
"Get Org entries matching TODO-KEYWORD followed by STRING in the heading.
Limit entries to those whole deadline/scheduled is equal or greater to
SINCE date.
Each entry is a plist of :heading, :contents, :started, :closed."
(or (delq nil
(org-map-entries
(lambda ()
(when-let* ((case-fold-search t)
(started (prot-org--timestamp-to-time (or (org-entry-get nil "DEADLINE") (org-entry-get nil "SCHEDULED"))))
(closed (prot-org--timestamp-to-time (org-entry-get nil "CLOSED")))
((re-search-forward (format "\\<%s\\>.*\\<%s\\>" todo-keyword string) (line-end-position) t 1))
((org-time-less-p since started)))
(list
:heading (org-get-heading :no-tags :no-todo :no-priority :no-comment)
:contents (org-get-entry)
:started started
:closed closed)))))
(user-error "No entries with heading matching `\\<%s\\>.*\\<%s\\>'" todo-keyword string)))
(defvar prot-org-coach--name-history nil
"Minibuffer history of `prot-org-coach--name-prompt'.")
(defun prot-org-coach--name-prompt ()
"Prompt for name of person."
(let ((default (car prot-org-coach--name-history)))
(read-string
(format-prompt "Name of person" default)
nil 'prot-org-coach--name-history default)))
;;;###autoload
(defun prot-org-coach-report (name since)
"Produce clock report for coaching with person of NAME.
SINCE is the date (of time 00:00) to count from until now."
(interactive
(list
(prot-org-coach--name-prompt)
(format "[%s]" (org-read-date))))
(if-let* ((since-object (prot-org--timestamp-to-time since))
(entries (prot-org-coach--get-entries "done" name since-object))
(buffer (get-buffer-create "*prot-org-coach-entries*")))
(with-current-buffer (pop-to-buffer buffer)
(erase-buffer)
(org-mode)
(dolist (entry entries)
(insert (format "* %s\n%s\n\n" (plist-get entry :heading) (plist-get entry :contents)))
(org-clock-in nil (plist-get entry :started))
(org-clock-out nil t (plist-get entry :closed)))
(goto-char (point-min))
(save-excursion
(insert (format "%s\n\n" prot-org-clock--template-no-effort)))
(save-excursion
(goto-char (line-end-position))
(insert (format " :tstart %S" since)))
(org-dblock-update))
(user-error "No entries for name `%s'" name)))
;;;; org-agenda
(declare-function calendar-day-name "calendar")
@ -175,6 +316,17 @@ produces dates with a fixed length."
(defvar org-priority-highest)
(defun prot-org-agenda-include-priority-no-timestamp ()
"Return nil if heading has a priority but no timestamp.
Otherwise, return the buffer position from where the search should
continue, per `org-agenda-skip-function'."
(let ((point (point)))
(if (and (eq (nth 3 (org-heading-components)) ?A)
(not (org-get-deadline-time point))
(not (org-get-scheduled-time point)))
nil
(line-beginning-position 2))))
(defvar prot-org-custom-daily-agenda
;; NOTE 2021-12-08: Specifying a match like the following does not
;; work.
@ -185,10 +337,15 @@ produces dates with a fixed length."
;; `org-agenda-skip-function'.
`((tags-todo "*"
((org-agenda-overriding-header "Important tasks without a date\n")
(org-agenda-skip-function '(org-agenda-skip-if nil '(timestamp)))
(org-agenda-skip-function
`(org-agenda-skip-entry-if
'notregexp ,(format "\\[#%s\\]" (char-to-string org-priority-highest))))
;; NOTE 2024-10-31: Those used to work, but now the
;; query for the timestamp is ignored. I thus wrote
;; `prot-org-agenda-include-priority-no-timestamp'.
;;
;; (org-agenda-skip-function '(org-agenda-skip-subtree-if nil '(timestamp)))
;; (org-agenda-skip-function
;; `(org-agenda-skip-entry-if
;; 'notregexp ,(format "\\[#%s\\]" (char-to-string org-priority-highest))))
(org-agenda-skip-function #'prot-org-agenda-include-priority-no-timestamp)
(org-agenda-block-separator nil)))
(agenda "" ((org-agenda-overriding-header "\nPending scheduled tasks")
(org-agenda-time-grid nil)
@ -250,6 +407,29 @@ produces dates with a fixed length."
(org-agenda-skip-function '(org-agenda-skip-entry-if 'todo 'done)))))
"Custom agenda for use in `org-agenda-custom-commands'.")
(defun prot-org-custom-agenda-prot-asks-get-date ()
"Return the timestamp of the current heading or nil.
For use in `prot-org-custom-prot-asks-agenda'."
(when-let* ((timestamp (or (org-entry-get nil "DEADLINE") (org-entry-get nil "SCHEDULED")))
(time (prot-org--timestamp-to-time timestamp)))
(format-time-string "%10A %e %10B %6R" time)))
(defvar prot-org-custom-prot-asks-agenda
'((tags-todo "protasks"
((org-agenda-overriding-header "Prot Asks\n")
(org-agenda-prefix-format '((tags . "%(prot-org-custom-agenda-prot-asks-get-date) ")))
(org-agenda-sorting-strategy '(deadline-up))
(org-agenda-remove-tags t)
(org-agenda-block-separator nil))))
"Custom agenda for use in `org-agenda-custom-commands'.")
(defun prot-org-agenda-set-outline ()
"Set `outline-regexp' for my Org agenda buffers."
(when (derived-mode-p 'org-agenda-mode)
(setq-local outline-regexp "^\\([\s\t]+\\)\\([A-Z]+ \\|[0-9]+:[0-9]+ \\)\\(Deadline\\|Scheduled\\)")))
(add-hook 'org-agenda-mode-hook #'prot-org-agenda-set-outline)
;;;;; agenda appointments
(defvar prot-org-agenda-after-edit-hook nil
@ -325,11 +505,29 @@ If the entry already has a CUSTOM_ID, return it as-is, else
create a new one."
(let* ((pos (point))
(id (org-entry-get pos "CUSTOM_ID")))
(if (and id (stringp id) (string-match-p "\\S-" id))
id
(setq id (org-id-new "h"))
(org-entry-put pos "CUSTOM_ID" id)
id)))
(or (and id (stringp id) (string-match-p "\\S-" id))
(and (setq id (org-id-new "h")) (org-entry-put pos "CUSTOM_ID" id)))
id))
(defun prot-org--heading-to-id ()
"Convert current heading text to an ID for CUSTOM_ID purposes."
(thread-last
(org-get-heading :no-tags :no-todo :no-priority :no-comment)
(replace-regexp-in-string "[][{}!@#$%^&*()+'\"?,.\|;:~`‘’“”/=]*" "")
(replace-regexp-in-string "\s" "-")
(string-trim)
(downcase)
(concat "h:")))
(defun prot-org--id-get-readable (&optional force)
"Like `prot-org--id-get' but use the heading wording to create and ID.
With optional FORCE, update the value even if one exists."
(let* ((pos (point))
(id (unless force (org-entry-get pos "CUSTOM_ID"))))
(or (and id (stringp id) (string-match-p "\\S-" id))
(and (setq id (prot-org--heading-to-id))
(org-entry-put pos "CUSTOM_ID" id)))
id))
(declare-function org-map-entries "org")
@ -337,14 +535,93 @@ create a new one."
(defun prot-org-id-headlines ()
"Add missing CUSTOM_ID to all headlines in current file."
(interactive)
(org-map-entries
(lambda () (prot-org--id-get))))
(org-map-entries (lambda () (prot-org--id-get))))
;;;###autoload
(defun prot-org-id-headline ()
"Add missing CUSTOM_ID to headline at point."
(defun prot-org-id-headlines-readable (&optional force)
"Like `prot-org-id-headlines' but with readable IDs.
A readable identifier is one derived from the text of the heading. In
theory, this may not be unique.
With optional FORCE, update the value even if one exists."
(interactive "P")
(org-map-entries (lambda () (prot-org--id-get-readable force))))
;;;###autoload
(defun prot-org-id-headline (&optional readable)
"Add missing CUSTOM_ID to headline at point.
With optional prefix argument READABLE get a readable identifier derived
from the heading text instead of a UUID."
(interactive "P")
(funcall (if readable 'prot-org--id-get-readable 'prot-org--id-get)))
;;;###autoload
(defun prot-org-get-dotemacs-link ()
"Get URL to current heading in my dotemacs file."
(declare (interactive-only t))
(interactive)
(prot-org--id-get))
(if (and (derived-mode-p 'org-mode)
(string-match-p "prot-emacs\\.org\\'" buffer-file-name))
(if-let* ((id (org-entry-get (point) "CUSTOM_ID"))
(url (concat "https://protesilaos.com/emacs/dotemacs#" id)))
(progn
(kill-new url)
(message "Copied %s" (propertize url 'face 'success)))
(error "No CUSTOM_ID for the current entry"))
(user-error "You are not in the right file")))
;;;; Org and Diary integration
(defun prot-org-diary--get-file (ics-file)
"Return diary file path that reuses the name of ICS-FILE.
The path is relative to the `diary-file'."
(unless (file-exists-p diary-file)
(error "The `diary-file' does not exist"))
(let* ((directory (file-name-directory diary-file))
(ics-name (file-name-sans-extension (file-name-nondirectory ics-file)))
(diary-name (format "%s%s-%s-diary.txt" directory (format-time-string "%Y-%m-%d") ics-name)))
(when-let* ((buffer (get-file-buffer diary-name)))
(with-current-buffer buffer
(erase-buffer)))
(when (file-exists-p diary-name)
(delete-file diary-name))
diary-name))
(defun prot-org-diary-include-file (diary-file-to-include)
"Include DIARY-FILE-TO-INCLUDE in the `diary-file'."
(unless (file-exists-p diary-file)
(error "The `diary-file' does not exist"))
(with-current-buffer (find-file-noselect diary-file)
(let ((include-directive (format "#include %S" diary-file-to-include)))
(save-excursion
(goto-char (point-min))
(while (re-search-forward (format "^%s.*" include-directive) nil t)
(delete-region (match-beginning 0) (match-end 0)))
(goto-char (point-max))
(insert "\n")
(insert include-directive)
(insert "\n")
(save-buffer)))))
;;;###autoload
(defun prot-org-diary-import-ics (ics-file diary-file-to-include)
"Import ICS-FILE to DIARY-FILE."
(interactive
(let ((ics-file (read-file-name "Which ICS file? ")))
(list
ics-file
(prot-org-diary--get-file ics-file))))
(save-window-excursion
(icalendar-import-file ics-file diary-file-to-include))
(let ((diary-file-propertized (propertize diary-file-to-include 'face 'success))
(main-diary-file-propertized (propertize diary-file 'face 'warning)))
(when (yes-or-no-p (format
"Imported `%s' as `%s'\nINCLUDE IT in `%s'?"
(propertize ics-file 'face 'error)
diary-file-propertized
main-diary-file-propertized))
(prot-org-diary-include-file diary-file-to-include)
(message "Appended `%s' to `%s'" diary-file-propertized main-diary-file-propertized))))
(provide 'prot-org)
;;; prot-org.el ends here

View file

@ -1,8 +1,8 @@
;;; prot-pair.el --- Insert character pair around symbol or region -*- lexical-binding: t -*-
;; Copyright (C) 2023 Protesilaos Stavrou
;; Copyright (C) 2023-2026 Protesilaos
;; Author: Protesilaos Stavrou <info@protesilaos.com>
;; Author: Protesilaos <info@protesilaos.com>
;; URL: https://protesilaos.com/emacs/dotemacs
;; Version: 0.1.0
;; Package-Requires: ((emacs "30.1"))
@ -26,11 +26,6 @@
;;
;; Insert character pair around symbol or region using minibuffer
;; completion.
;;
;; Remember that every piece of Elisp that I write is for my own
;; educational and recreational purposes. I am not a programmer and I
;; do not recommend that you copy any of this if you are not certain of
;; what it does.
;;; Code:
@ -39,8 +34,8 @@
:group 'editing)
(defcustom prot-pair-pairs
'((?' :description "Single quotes" :pair (?' . ?'))
(?\" :description "Double quotes" :pair (?\" . ?\"))
'((?' :description "Single quotes" :pair ?')
(?\" :description "Double quotes" :pair ?\")
(? :description "Single curly quotes" :pair (? . ?))
(?“ :description "Double curly quotes" :pair (?“ . ?”))
(?\> :description "Natural language quotes" :pair prot-pair-insert-natural-language-quotes)
@ -48,20 +43,20 @@
(?{ :description "Curly brackets" :pair (?{ . ?}))
(?\[ :description "Square brackets" :pair (?\[ . ?\]))
(?\< :description "Angled brackets" :pair (?\< . ?\>))
(?@ :description "At signs" :pair (?@ . ?@))
(?= :description "Equals signs" :pair (?= . ?=))
(?+ :description "Plus signs" :pair (?+ . ?+))
(?@ :description "At signs" :pair ?@)
(?= :description "Equals signs" :pair ?=)
(?+ :description "Plus signs" :pair ?+)
(?` :description "Backticks" :pair prot-pair-insert-backticks)
(?~ :description "Tildes" :pair (?~ . ?~))
(?* :description "Asterisks" :pair (?* . ?*))
(?/ :description "Forward slashes" :pair (?/ . ?/))
(?_ :description "Underscores" :pair (?_ . ?_)))
(?~ :description "Tildes" :pair ?~)
(?* :description "Asterisks" :pair ?*)
(?/ :description "Forward slashes" :pair ?/)
(?_ :description "Underscores" :pair ?_))
"Alist of pairs for use with `prot-pair-insert'.
Each element in the list is a list whose `car' is a character and
the `cdr' is a plist with a `:description' and `:pair' keys. The
`:description' is a string used to describe the character/pair in
interactive use, while `:pair' is a cons cell referencing the
opening and closing characters.
interactive use, while `:pair' is either a cons cell referencing the
opening and closing characters or a single character.
The value of `:pair' can also be the unquoted symbol of a
function. The function is called with no arguments and must
@ -98,27 +93,29 @@ backtick and single quote, else two backticks."
(defun prot-pair--annotate (character)
"Annotate CHARACTER with its description in `prot-pair-pairs'."
(when-let ((char (if (characterp character) character (string-to-char character)))
(plist (alist-get char prot-pair-pairs))
(description (plist-get plist :description)))
(when-let* ((char (if (characterp character) character (string-to-char character)))
(plist (alist-get char prot-pair-pairs))
(description (plist-get plist :description)))
(format " %s" description)))
(defun prot-pair--get-pair (character)
"Get the pair of corresponding to CHARACTER."
(when-let ((char (if (characterp character) character (string-to-char character)))
(plist (alist-get char prot-pair-pairs))
(pair (plist-get plist :pair)))
pair))
"Get the pair corresponding to CHARACTER."
(when-let* ((plist (alist-get character prot-pair-pairs))
(pair-value (plist-get plist :pair)))
(if (characterp pair-value)
(cons pair-value pair-value)
pair-value)))
(defun prot-pair--insert-prompt ()
"Prompt for pair among `prot-pair-pairs'."
(let ((default (car prot-pair--insert-history))
(candidates (mapcar (lambda (char) (char-to-string (car char))) prot-pair-pairs))
(completion-extra-properties `(:annotation-function ,#'prot-pair--annotate)))
(completing-read
(format-prompt "Select pair" default)
candidates nil :require-match
nil 'prot-pair--insert-history default)))
(let* ((default (car prot-pair--insert-history))
(candidates (mapcar (lambda (char) (char-to-string (car char))) prot-pair-pairs))
(completion-extra-properties `(:annotation-function ,#'prot-pair--annotate))
(choice (completing-read
(format-prompt "Select pair" default)
candidates nil :require-match
nil 'prot-pair--insert-history default)))
(string-to-char choice)))
(defun prot-pair--insert-bounds ()
"Return boundaries of symbol at point or active region."
@ -126,29 +123,44 @@ backtick and single quote, else two backticks."
(cons (region-beginning) (region-end))
(bounds-of-thing-at-point 'symbol)))
(defun prot-pair-insert-subr (character n)
"Insert pair of CHARACTER as many as N times."
(if-let* ((pair (prot-pair--get-pair character)))
(let* ((bounds (prot-pair--insert-bounds))
(beg (car bounds))
(end (1+ (cdr bounds))) ; 1+ because we want the character after it
(characters (if (functionp pair) (funcall pair) pair)))
(dotimes (_ n)
(save-excursion
(goto-char beg)
(insert (car characters))
(goto-char end)
(setq end (1+ end))
(insert (cdr characters))))
(goto-char (+ end (1- n))))
(user-error "Cannot find character `%s' in `prot-pair-pairs'" (char-to-string character))))
;;;###autoload
(defun prot-pair-insert (pair n)
"Insert N number of PAIR around object at point.
PAIR is one among `prot-pair-pairs'. The object at point is
either a symbol or the boundaries of the active region. N is a
numeric prefix argument, defaulting to 1 if none is provided in
interactive use."
(defun prot-pair-insert (character n)
"Insert CHARACTER pair an N number of times around object at point.
CHARACTER is one among `prot-pair-pairs'. The object at point is either
a symbol or the boundaries of the active region. N is a numeric prefix
argument, defaulting to 1 if none is provided in interactive use."
(interactive
(list
(prot-pair--get-pair (prot-pair--insert-prompt))
(prot-pair--insert-prompt)
(prefix-numeric-value current-prefix-arg)))
(let* ((bounds (prot-pair--insert-bounds))
(beg (car bounds))
(end (1+ (cdr bounds))) ; 1+ because we want the character after it
(characters (if (functionp pair) (funcall pair) pair)))
(dotimes (_ n)
(save-excursion
(goto-char beg)
(insert (car characters))
(goto-char end)
(setq end (1+ end))
(insert (cdr characters))))
(goto-char (+ end (1- n)))))
(prot-pair-insert-subr character n))
;;;###autoload
(defun prot-pair-insert-directly (character n)
"Read CHARACTER, find it in `prot-pair-pairs', and insert it outright.
Do not use minibuffer completion like `prot-pair-insert'."
(interactive
(list
(read-char)
(prefix-numeric-value current-prefix-arg)))
(prot-pair-insert-subr character n))
;;;###autoload
(defun prot-pair-delete ()

View file

@ -1,8 +1,8 @@
;;; prot-prefix.el --- Prefix keymap for my dotemacs -*- lexical-binding: t -*-
;; Copyright (C) 2023 Protesilaos Stavrou
;; Copyright (C) 2023-2026 Protesilaos
;; Author: Protesilaos Stavrou <info@protesilaos.com>
;; Author: Protesilaos <info@protesilaos.com>
;; URL: https://protesilaos.com/emacs/dotemacs
;; Version: 0.1.0
;; Package-Requires: ((emacs "30.1"))
@ -25,11 +25,6 @@
;;; Commentary:
;;
;; Prefix keymap for my custom keymaps.
;;
;; Remember that every piece of Elisp that I write is for my own
;; educational and recreational purposes. I am not a programmer and I
;; do not recommend that you copy any of this if you are not certain of
;; what it does.
;;; Code:
@ -185,9 +180,9 @@
;; some do not. The former are those which define a prefix command
;; (per `define-prefix-command'). This is a symbol that references
;; the keymaps, thus making our binding an indirection: if we update
;; the key map, we automatically get the new key bindings. Whereas
;; the keymap, we automatically get the new key bindings. Whereas
;; when we bind a key to the value of a variable, we have to update
;; the key map and then the binding for changes to propagate.
;; the keymap and then the binding for changes to propagate.
(defvar-keymap prot-prefix-map
:doc "Prefix keymap with multiple subkeymaps."
:name "Prot Prefix"

View file

@ -1,8 +1,8 @@
;;; prot-project.el --- Extensions for project.el -*- lexical-binding: t -*-
;; Copyright (C) 2024 Protesilaos Stavrou
;; Copyright (C) 2025-2026 Protesilaos
;; Author: Protesilaos Stavrou <info@protesilaos.com>
;; Author: Protesilaos <info@protesilaos.com>
;; URL: https://protesilaos.com/emacs/dotemacs
;; Version: 0.1.0
;; Package-Requires: ((emacs "30.1"))
@ -25,11 +25,6 @@
;;; Commentary:
;;
;; Extensions for project.el.
;;
;; Remember that every piece of Elisp that I write is for my own
;; educational and recreational purposes. I am not a programmer and I
;; do not recommend that you copy any of this if you are not certain of
;; what it does.
;;; Code:
@ -57,6 +52,9 @@ With optional COMMAND, run it in DIRECTORY."
"Return a list of frame names."
(mapcar #'car (make-frame-names-alist)))
(defvar prot-project-switch-hook nil
"Normal hook called after `prot-project-switch'.")
;;;###autoload
(defun prot-project-switch (directory)
"Switch to project DIRECTORY.
@ -67,15 +65,17 @@ the project in DIRECTORY using `project-dired'."
(let ((name (file-name-nondirectory (directory-file-name directory))))
(if (member name (prot-project--frame-names))
(select-frame-by-name name)
(prot-project--switch directory 'project-dired))))
(prot-project--switch directory 'project-dired))
(run-hooks 'prot-project-switch-hook)
(setq this-command 'project-switch-project)))
;;;; Produce a VC root log for the project
(defun prot-project-rename-vc-root-log (&rest _)
"Rename the buffer of `vc-print-root-log' to mention the project."
(when-let ((root (vc-root-dir))
((consp project--list))
((member root (mapcar #'car project--list))))
(when-let* ((root (vc-root-dir))
((consp project--list))
((member root (mapcar #'car project--list))))
(rename-buffer (format "*vc-root-log: %s*" root))))
(advice-add #'vc-print-root-log :after #'prot-project-rename-vc-root-log)
@ -98,7 +98,7 @@ If FRAME is nil, use the current frame."
(tab-new)
(find-file directory)
(unwind-protect
(prot-project--switch directory)
(prot-project--switch directory 'project-dired)
(tab-rename name)
;; NOTE 2024-01-15 06:52 +0200: I am adding this because
;; `tab-rename' is not persistent for some reason. Probably a bug...
@ -119,7 +119,19 @@ Use this as an alternative to `project-switch-project'."
(let ((name (file-name-nondirectory (directory-file-name directory))))
(if (member name (prot-project-in-tab--get-tab-names))
(tab-switch name)
(prot-project-in-tab--create-tab directory name))))
(prot-project-in-tab--create-tab directory name))
(setq this-command 'project-switch-project)))
;;;###autoload
(defun prot-project-maybe-in-tab ()
"Switch to project depending on `beframe-mode'.
When the mode is enabled (the expected behaviour), use the command
`prot-project-switch'. Otherwise, do `prot-project-in-tab'."
(interactive)
(call-interactively
(if (bound-and-true-p beframe-mode)
'prot-project-switch
'prot-project-in-tab)))
;;;; Set up a project root
@ -135,7 +147,7 @@ is a string."
(file-symbol (intern file)))
`(progn
(defun ,project-find-fn (dir)
(when-let ((root (locate-dominating-file dir ,file)))
(when-let* ((root (locate-dominating-file dir ,file)))
(cons ',file-symbol root)))
(cl-defmethod project-root ((project (head ,file-symbol)))
@ -146,5 +158,19 @@ is a string."
(add-hook ',(intern (format "%s-hook" mode)) #',major-mode-fn))))
;;;; Enable completion in the `compile' command
(defun prot-project-compilation-read-command (command)
"Like `compilation-read-command' for COMMAND with completion against the `compile-history'."
(if-let* ((history compile-history)
(default (car history)))
(completing-read "Compile command: " history nil nil nil 'compile-history default)
(read-shell-command "Compile command: " command
(if (equal (car compile-history) command)
'(compile-history . 1)
'compile-history))))
(advice-add #'compilation-read-command :override #'prot-project-compilation-read-command)
(provide 'prot-project)
;;; prot-project.el ends here

View file

@ -0,0 +1,155 @@
;;; prot-register.el --- Extensions for project.el -*- lexical-binding: t -*-
;; Copyright (C) 2025-2026 Protesilaos
;; Author: Protesilaos <info@protesilaos.com>
;; URL: https://protesilaos.com/emacs/dotemacs
;; Version: 0.1.0
;; Package-Requires: ((emacs "30.1"))
;; This file is NOT part of GNU Emacs.
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or (at
;; your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;;
;; Extensions for register.el.
;;; Code:
(require 'register)
;;;; Define new register for file with point
(cl-defmethod register--type ((_regval vector)) 'vector)
(cl-defmethod register-val-describe ((val vector) _verbose)
(if-let* ((position (aref val 2))
(file (aref val 1)))
(princ (format "%s at position %s" file position))
(princ "Garbage data")))
;;;###autoload
(defun prot-simple-file-to-register (register)
"Store current location of file's point in REGISTER."
(interactive (list (register-read-with-preview "File with point to register: ")))
(set-register register (vector 'file-with-point (buffer-file-name) (point))))
(defvar prot-simple-file-to-register-jump-hook nil
"Normal hook called after jumping to a file register.
See `prot-simple-file-to-register'.")
;;;###autoload
(cl-defmethod register-val-jump-to ((val vector) delete)
"Handle how to jump to a location register.
This is like the default, but does not ask to visit a file: it does it
outright."
(cond
((eq (aref val 0) 'file-with-point)
(find-file (aref val 1))
(goto-char (aref val 2))
(run-hooks 'prot-simple-file-to-register-jump-hook))
(t (cl-call-next-method val delete))))
;;;; Do-What-I-Mean commands
;;;###autoload
(defun prot-register-add-dwim (register &optional number)
"Do-What-I-Mean with REGISTER.
If the region is active, call `copy-to-register'.
If the optional prefix argument NUMBER is non-nil, then call
`number-to-register'.
If there are more than two windows or there are more than one
`tab-bar-mode' tabs, then do `frameset-to-register'.
Otherwise, use `prot-simple-file-to-register'.
Also see `prot-register-use-dwim'."
(interactive
(list
(register-read-with-preview "Add register: ")
(when current-prefix-arg
(prefix-numeric-value current-prefix-arg))))
(cond
((when-let* ((_ (region-active-p))
(beg (region-beginning))
(end (region-end))
(text (buffer-substring beg end))
(_ (not (string-blank-p text))))
(copy-to-register register beg end)
(message "Copied `%s' to register `%c'" text register)))
(number
(number-to-register number register)
(message "Copied number `%d' to register `%c'" number register))
((or (length> (window-list) 2)
(and (bound-and-true-p tab-bar-mode)
(length> (tab-bar-tabs) 1)))
(frameset-to-register register)
(message "Copied current frameset to register `%c'" register))
(t
(prot-simple-file-to-register register)
(message "Copied current point in file to register `%c'" register))))
;;;###autoload
(defun prot-register-use-dwim (register)
"Use the REGISTER, jumping or inserting it, depending on its ocntents."
(interactive (list (register-read-with-preview "Use register: ")))
(let ((contents (get-register register)))
(cond
((register--jumpable-p contents)
(jump-to-register register))
((or (register--insertable-p contents)
(numberp register))
(insert-register register))
(t
(error "The register is unknown: %S" contents)))))
;;;; The `prot-register-persist-mode'
(defcustom prot-register-save-file (locate-user-emacs-file "prot-register.eld")
"File to store registers to and retrieve from."
:type 'file)
(defun prot-register-store ()
"Write `register-alist' to `prot-register-save-file'."
(with-temp-file prot-register-save-file
(insert ";; Auto-generated file: DO NOT EDIT -*- mode: emacs-lisp -*-\n")
(pp register-alist (current-buffer))))
(defun prot-register-load ()
"Read `prot-register-save-file' and return its contents."
(with-temp-buffer
(when (file-exists-p prot-register-save-file)
(insert-file-contents prot-register-save-file)
(read (buffer-string)))))
(defun prot-register-watcher (symbol newval operation where)
(when (and (eq operation 'set) (null where) newval)
(prot-register-store)))
;;;###autoload
(define-minor-mode prot-register-persist-mode
"When enabled save `register-alist' to `prot-register-save-file' when a change occurs."
:globat t
(if prot-register-persist-mode
(progn
(add-variable-watcher 'register-alist #'prot-register-watcher)
(add-hook 'kill-emacs-hook #'prot-register-store))
(remove-variable-watcher 'register-alist #'prot-register-watcher)
(remove-hook 'kill-emacs-hook #'prot-register-store)))
(provide 'prot-register)
;;; prot-register.el ends here

View file

@ -1,8 +1,8 @@
;;; prot-scratch.el --- Scratch buffers for editable major mode of choice -*- lexical-binding: t -*-
;; Copyright (C) 2023 Protesilaos Stavrou
;; Copyright (C) 2023-2026 Protesilaos
;; Author: Protesilaos Stavrou <info@protesilaos.com>
;; Author: Protesilaos <info@protesilaos.com>
;; URL: https://protesilaos.com/emacs/dotemacs
;; Version: 0.1.0
;; Package-Requires: ((emacs "30.1"))
@ -27,11 +27,6 @@
;; Set up a scratch buffer for an editable major mode of choice. The
;; idea is based on the `scratch.el' package by Ian Eure:
;; <https://github.com/ieure/scratch-el>.
;;
;; Remember that every piece of Elisp that I write is for my own
;; educational and recreational purposes. I am not a programmer and I
;; do not recommend that you copy any of this if you are not certain of
;; what it does.
;;; Code:

View file

@ -1,8 +1,8 @@
;;; prot-search.el --- Extensions to isearch, replace, grep for my dotemacs -*- lexical-binding: t -*-
;; Copyright (C) 2020-2023 Protesilaos Stavrou
;; Copyright (C) 2020-2026 Protesilaos
;; Author: Protesilaos Stavrou <info@protesilaos.com>
;; Author: Protesilaos <info@protesilaos.com>
;; URL: https://protesilaos.com/emacs/dotemacs
;; Version: 0.1.0
;; Package-Requires: ((emacs "30.1"))
@ -26,11 +26,6 @@
;;
;; This covers my isearch.el, replace.el, and grep.el extensions, for
;; use in my Emacs setup: <https://protesilaos.com/emacs/dotemacs>.
;;
;; Remember that every piece of Elisp that I write is for my own
;; educational and recreational purposes. I am not a programmer and I
;; do not recommend that you copy any of this if you are not certain of
;; what it does.
;;; Code:
@ -94,51 +89,6 @@ search started."
(isearch-pop-state)))
(isearch-update))
;;;###autoload
(defun prot-search-isearch-repeat-forward (&optional arg)
"Move forward, keeping point at the beginning of the match.
Optionally move to ARGth match in the given direction."
(interactive "p")
(when (and isearch-forward isearch-other-end)
(goto-char isearch-other-end))
(isearch-repeat-forward (or arg 1)))
;;;###autoload
(defun prot-search-isearch-repeat-backward (&optional arg)
"Move backward, keeping point at the beginning of the match.
Optionally move to ARGth match in the given direction."
(interactive "p")
(when (and (not isearch-forward) isearch-other-end)
(goto-char isearch-other-end))
(isearch-repeat-backward (or arg 1)))
(defmacro prot-search-isearch-occurrence (name edge &optional doc)
"Construct function for moving to `isearch' occurrence.
NAME is the name of the function. EDGE is either the beginning
or the end of the buffer. Optional DOC is the resulting
function's docstring."
`(defun ,name (&optional arg)
,doc
(interactive "p")
(let ((x (or arg 1))
(command (intern (format "isearch-%s-of-buffer" ,edge))))
(isearch-forward-symbol-at-point)
(funcall command x))))
(prot-search-isearch-occurrence
prot-search-isearch-beginning-of-buffer
"beginning"
"Run `isearch-beginning-of-buffer' for the symbol at point.
With numeric ARG, move to ARGth occurrence counting from the
beginning of the buffer.")
(prot-search-isearch-occurrence
prot-search-isearch-end-of-buffer
"end"
"Run `isearch-end-of-buffer' for the symbol at point.
With numeric ARG, move to ARGth occurrence counting from the
end of the buffer.")
;;;; Replace/Occur
(defvar prot-search-markup-replacements
@ -195,6 +145,31 @@ boundaries, else start from point to the end of the buffer."
;; (isearch-forward-symbol-at-point)
;; (isearch-query-replace-regexp))
(defvar prot-search-collect-history nil
"Minibuffer history for `prot-search-collect'.")
;;;###autoload
(defun prot-search-collect (regexp)
"Return matches for REGEXP as a substring in the current buffer.
Put the results in a *prot-search-collect* buffer."
(interactive
(list
(format "\\<\\w*%s\\w*\\>" (read-string "Collect REGEXP: " nil 'prot-search-collect-history))))
(save-excursion
(goto-char (point-min))
(let ((matches nil))
(while (re-search-forward regexp nil t)
(push (match-string 0) matches))
(when matches
(setq matches (nreverse matches))
(let ((buffer (get-buffer-create "*prot-search-collect*")))
(with-current-buffer buffer
(erase-buffer)
(dolist (match matches)
(insert match)
(insert "\n")))
(display-buffer buffer))))))
(autoload 'goto-address-mode "goto-addr")
;;;###autoload
@ -333,9 +308,10 @@ Also see `prot-search-grep-todo-keywords'."
(defun prot-search-outline ()
"Go to the line of the given outline using completion."
(interactive)
(when-let ((selection (prot-search--outline-prompt))
(line (string-to-number (car (split-string selection "\t")))))
(goto-line line)
(when-let* ((selection (prot-search--outline-prompt))
(line (string-to-number (car (split-string selection "\t")))))
(goto-char (point-min))
(forward-line (- line 1))
(run-hooks 'prot-search-outline-hook)))
;;;; Grep
@ -528,18 +504,22 @@ and function as links to the context they reference."
prot-search--find-file-names-args
"dired")
;; (defun prot-search-find-grep-file (regexp)
;; "Use find to produce list of files that include REGEXP."
;; (interactive
;; (list
;; (read-regexp "Find and grep for REGEXP: " nil 'prot-search--find-grep-hist)))
;; (let ((files (process-lines "find"
;; "-type" "f"
;; "-exec" "grep" "-nHE" "--color=auto" (format "'%s" regexp) " "
;; "-ls" " "
;; "{};")
;; ))
;; (find-file (completing-read "Find file: "files))))
;; TODO 2026-08-15: I want to revise all of the above functions that
;; involve find/grep. Is `process-lines' the better option?
;;;###autoload
(defun prot-search-grep-to-dired (regexp)
"Produce a Dired buffer with files matching REGEXP.
Recursively search for files from the current directory using the find
program."
(interactive
(list
(read-regexp "Find and grep for REGEXP: " nil 'prot-search--find-grep-hist)))
(let ((files (process-lines "find"
"-type" "f"
"-exec" "grep" "-nHE" "--color=auto" regexp
"-ls" "{}" ";")))
(dired (cons default-directory files))))
(provide 'prot-search)
;;; prot-search.el ends here

View file

@ -1,8 +1,8 @@
;;; prot-shell.el --- M-x shell extensions for my dotemacs -*- lexical-binding: t -*-
;; Copyright (C) 2023 Protesilaos Stavrou
;; Copyright (C) 2023-2026 Protesilaos
;; Author: Protesilaos Stavrou <info@protesilaos.com>
;; Author: Protesilaos <info@protesilaos.com>
;; URL: https://protesilaos.com/emacs/dotemacs
;; Version: 0.1.0
;; Package-Requires: ((emacs "30.1"))
@ -26,11 +26,6 @@
;;
;; This covers my shell.el extensions, for use in my Emacs setup:
;; <https://protesilaos.com/emacs/dotemacs>.
;;
;; Remember that every piece of Elisp that I write is for my own
;; educational and recreational purposes. I am not a programmer and I
;; do not recommend that you copy any of this if you are not certain of
;; what it does.
;;; Code:
@ -109,18 +104,24 @@ Only account for the history Emacs knows about, ignoring
(defun prot-shell-track-cd (&rest _)
"Track shell input of cd commands.
Push `shell-last-dir' to `prot-shell-cd-directories'."
(when-let ((input (prot-shell--last-input))
((string-match-p "cd " input)))
(when-let* ((input (prot-shell--last-input))
((string-match-p "cd " input)))
(push shell-last-dir prot-shell-cd-directories)))
(defun prot-shell-update-name-on-cd (&rest _)
"Update the shell buffer name after a cd for use in `prot-shell'."
(when-let* ((input (prot-shell--last-input))
((string-match-p "cd " input)))
(rename-buffer (format "*prot-shell in %s*" default-directory) :make-unique)))
(defvar prot-shell--cd-history nil
"Minibuffer history for `prot-shell-cd'.")
(defun prot-shell--cd-prompt ()
"Prompt for a directory among `prot-shell-cd-directories'."
(if-let ((history prot-shell-cd-directories)
(dirs (cons default-directory history))
(def (if (listp dirs) (car dirs) shell-last-dir)))
(if-let* ((history prot-shell-cd-directories)
(dirs (cons default-directory history))
(def (if (listp dirs) (car dirs) shell-last-dir)))
(completing-read
(format-prompt "Select directory" def)
dirs nil :require-match nil 'prot-shell--cd-history def)
@ -146,101 +147,104 @@ Push `shell-last-dir' to `prot-shell-cd-directories'."
(defun prot-shell-cd-vc-root-dir ()
"Change into the `vc-root-dir'."
(interactive)
(if-let ((root (prot-shell--get-vc-root-dir)))
(if-let* ((root (prot-shell--get-vc-root-dir)))
(prot-shell--insert-and-send "cd" root)
(user-error "Cannot find the VC root of `%s'" default-directory)))
;;;; Bookmark support
;; NOTE 2025-06-23: Emacs 31 supports shell bookmarks, so I no longer
;; need this. I am keeping it here for reference.
;; NOTE 2023-08-18: I sent this to the Emacs maintainers as a patch
;; (bug#65039). I received approval to proceed with the change, but I
;; did not do it because a user reported an issue with SSH (TRAMP). I
;; do not have access to SSH and am not familiar with such workflows.
;; If/when that changes, I will try again. In the meantime, this is
;; good code and it works for me.
;; ;;;; Bookmark support
;;
;; ;; NOTE 2023-08-18: I sent this to the Emacs maintainers as a patch
;; ;; (bug#65039). I received approval to proceed with the change, but I
;; ;; did not do it because a user reported an issue with SSH (TRAMP). I
;; ;; do not have access to SSH and am not familiar with such workflows.
;; ;; If/when that changes, I will try again. In the meantime, this is
;; ;; good code and it works for me.
;;
;; ;; Adapted from esh-mode.el
;; (declare-function bookmark-prop-get "bookmark" (bookmark prop))
;;
;; (defun prot-shell-bookmark-name ()
;; "Return name of bookmark based on currect directory."
;; (format "prot-shell-%s"
;; (file-name-nondirectory
;; (directory-file-name
;; (file-name-directory default-directory)))))
;;
;; (defvar sh-shell-file)
;;
;; (defun prot-shell-bookmark-make-record ()
;; "Create a bookmark for the current Shell buffer."
;; `(,(prot-shell-bookmark-name)
;; (location . ,default-directory)
;; (shell-file-name . ,sh-shell-file)
;; (handler . prot-shell-bookmark-jump)))
;;
;; ;;;###autoload
;; (defun prot-shell-bookmark-jump (bookmark)
;; "Default BOOKMARK handler for Shell buffers."
;; (let ((default-directory (bookmark-prop-get bookmark 'location))
;; (explicit-shell-file-name (bookmark-prop-get bookmark 'shell-file-name)))
;; (shell (get-buffer-create (car bookmark)))))
;;
;; (put 'prot-shell-bookmark-jump 'bookmark-handler-type "Shell")
;; Adapted from esh-mode.el
(declare-function bookmark-prop-get "bookmark" (bookmark prop))
(defun prot-shell-bookmark-name ()
"Return name of bookmark based on currect directory."
(format "prot-shell-%s"
(file-name-nondirectory
(directory-file-name
(file-name-directory default-directory)))))
(defvar sh-shell-file)
(defun prot-shell-bookmark-make-record ()
"Create a bookmark for the current Shell buffer."
`(,(prot-shell-bookmark-name)
(location . ,default-directory)
(shell-file-name . ,sh-shell-file)
(handler . prot-shell-bookmark-jump)))
;;;###autoload
(defun prot-shell-bookmark-jump (bookmark)
"Default BOOKMARK handler for Shell buffers."
(let ((default-directory (bookmark-prop-get bookmark 'location))
(explicit-shell-file-name (bookmark-prop-get bookmark 'shell-file-name)))
(shell (get-buffer-create (car bookmark)))))
(put 'prot-shell-bookmark-jump 'bookmark-handler-type "Shell")
;;;; Convert YouTube links to Invidious
(defvar prot-shell-invidious-domains
'("invidious.io.lol"
"invidious.lunar.icu"
"iv.nboeck.de"
"vid.priv.au"
"invidious.tiekoetter.com"
"inv.in.projectsegfau.lt"
"onion.tube"
"yt.artemislena.eu"
"invidious.no-logs.com"
"yewtu.be"
"invidious.projectsegfau.lt"
"yt.oelrichsgarcia.de"
"invidious.0011.lt"
"inv.zzls.xyz"
"inv.bp.projectsegfau.lt"
"invidious.flokinet.to"
"iv.ggtyler.dev"
"invidious.slipfox.xyz"
"vid.puffyan.us"
"inv.pistasjis.net"
"inv.citw.lgbt"
"invidious.protokolla.fi"
"inv.makerlab.tech"
"inv.tux.pizza"
"invidious.privacydev.net")
"List of Invidious domains.")
(defvar prot-shell-youtube-domains
'("www.youtube.com"
"youtu.be")
"List of YouTube domains.")
(defvar prot-shell-yt-invidious-domains
(append prot-shell-youtube-domains
prot-shell-invidious-domains)
"List of YouTube and Invidious domains.")
(defun prot-shell--get-random-invidious-instance ()
"Return `random' index from `prot-shell-invidious-domains'."
(nth
(random (length prot-shell-invidious-domains))
prot-shell-invidious-domains))
;;;###autoload
(defun prot-shell-invidious ()
"Convert `prot-shell-yt-invidious-domains' into a random Invidious instance."
(interactive)
(save-excursion
(goto-char (line-beginning-position))
(while (re-search-forward (regexp-opt prot-shell-yt-invidious-domains) (line-end-position) :no-error)
(replace-match (prot-shell--get-random-invidious-instance)))))
;; ;;;; Convert YouTube links to Invidious
;;
;; (defvar prot-shell-invidious-domains
;; '("invidious.io.lol"
;; "invidious.lunar.icu"
;; "iv.nboeck.de"
;; "vid.priv.au"
;; "invidious.tiekoetter.com"
;; "inv.in.projectsegfau.lt"
;; "onion.tube"
;; "yt.artemislena.eu"
;; "invidious.no-logs.com"
;; "yewtu.be"
;; "invidious.projectsegfau.lt"
;; "yt.oelrichsgarcia.de"
;; "invidious.0011.lt"
;; "inv.zzls.xyz"
;; "inv.bp.projectsegfau.lt"
;; "invidious.flokinet.to"
;; "iv.ggtyler.dev"
;; "invidious.slipfox.xyz"
;; "vid.puffyan.us"
;; "inv.pistasjis.net"
;; "inv.citw.lgbt"
;; "invidious.protokolla.fi"
;; "inv.makerlab.tech"
;; "inv.tux.pizza"
;; "invidious.privacydev.net")
;; "List of Invidious domains.")
;;
;; (defvar prot-shell-youtube-domains
;; '("www.youtube.com"
;; "youtu.be")
;; "List of YouTube domains.")
;;
;; (defvar prot-shell-yt-invidious-domains
;; (append prot-shell-youtube-domains
;; prot-shell-invidious-domains)
;; "List of YouTube and Invidious domains.")
;;
;; (defun prot-shell--get-random-invidious-instance ()
;; "Return `random' index from `prot-shell-invidious-domains'."
;; (nth
;; (random (length prot-shell-invidious-domains))
;; prot-shell-invidious-domains))
;;
;; ;;;###autoload
;; (defun prot-shell-invidious ()
;; "Convert `prot-shell-yt-invidious-domains' into a random Invidious instance."
;; (interactive)
;; (save-excursion
;; (goto-char (line-beginning-position))
;; (while (re-search-forward (regexp-opt prot-shell-yt-invidious-domains) (line-end-position) :no-error)
;; (replace-match (prot-shell--get-random-invidious-instance)))))
;;;; Built-in Emacs commands
@ -298,14 +302,23 @@ input, otherwise perform buffer motion."
(interactive "^p")
(prot-shell--history-or-motion 'comint-next-input 'next-line arg))
;;;###autoload
(defun prot-shell ()
"Like `shell' but always start a new shell.
Name the shell buffer after the `default-directory'. If the name of
that buffer already exists, then reuse it."
(interactive)
(with-current-buffer (shell (format "*prot-shell in %s*" default-directory))
(add-hook 'comint-output-filter-functions #'prot-shell-update-name-on-cd nil :local)))
;;;; Minor mode setup
(defvar-keymap prot-shell-mode-map
:doc "Key map for `prot-shell-mode'."
:doc "keymap for `prot-shell-mode'."
"<up>" #'prot-shell-up-dwim
"<down>" #'prot-shell-down-dwim
"C-c C-d" #'prot-shell-cd
"C-c C-i" #'prot-shell-invidious
;; "C-c C-i" #'prot-shell-invidious
"C-c C-j" #'prot-shell-input-from-history
"C-c C-." #'prot-shell-cd-vc-root-dir
"C-c C-r" #'prot-shell-cd-vc-root-dir)

View file

@ -1,8 +1,8 @@
;;; prot-simple.el --- Common commands for my dotemacs -*- lexical-binding: t -*-
;; Copyright (C) 2020-2023 Protesilaos Stavrou
;; Copyright (C) 2020-2026 Protesilaos
;; Author: Protesilaos Stavrou <info@protesilaos.com>
;; Author: Protesilaos <info@protesilaos.com>
;; URL: https://protesilaos.com/emacs/dotemacs
;; Version: 0.1.0
;; Package-Requires: ((emacs "30.1"))
@ -25,11 +25,6 @@
;;; Commentary:
;;
;; Common commands for my Emacs: <https://protesilaos.com/emacs/dotemacs/>.
;;
;; Remember that every piece of Elisp that I write is for my own
;; educational and recreational purposes. I am not a programmer and I
;; do not recommend that you copy any of this if you are not certain of
;; what it does.
;;; Code:
@ -57,6 +52,39 @@ Used by `prot-simple-inset-date'."
;;;; General commands
;;;###autoload
(defun prot-simple-describe-at-point (symbol)
"Describe the SYMBOL at point.
If there is no symbol or the symbol at point does not satisfy `symbolp',
prompt for one."
(interactive (list (intern-soft (thing-at-point 'symbol))))
(if symbol
(describe-symbol symbol)
(call-interactively 'describe-symbol)))
;;;###autoload
(defun prot-simple-indent-dwim ()
"Indent the current defun in `prog-mode' or paragraph in `text-mode'."
(interactive)
(save-excursion
(cond
((derived-mode-p 'prog-mode)
(mark-defun))
((derived-mode-p 'text-mode)
(mark-paragraph)))
(indent-for-tab-command)
(deactivate-mark)))
;;;###autoload
(defun prot-simple-sudo ()
"Find the current file or directory using `sudo'."
(interactive)
(let ((destination (or buffer-file-name default-directory))
(auto-save-default nil))
(if (string= (file-remote-p destination 'method) "sudo")
(user-error "Already using `sudo'")
(find-file (format "/sudo::/%s" destination)))))
(defun prot-simple--mark (bounds)
"Mark between BOUNDS as a cons cell of beginning and end positions."
(push-mark (car bounds))
@ -72,11 +100,11 @@ expression."
(if (and (region-active-p)
(eq last-command this-command))
(ignore-errors (forward-sexp 1))
(when-let ((thing (cond
((thing-at-point 'url) 'url)
((thing-at-point 'sexp) 'sexp)
((thing-at-point 'string) 'string)
((thing-at-point 'word) 'word))))
(when-let* ((thing (cond
((thing-at-point 'url) 'url)
((thing-at-point 'sexp) 'sexp)
((thing-at-point 'string) 'string)
((thing-at-point 'word) 'word))))
(prot-simple--mark (bounds-of-thing-at-point thing)))))
;;;###autoload
@ -104,17 +132,24 @@ The DWIM behaviour of this command is as follows:
(t
(keyboard-quit))))
;; DEPRECATED 2023-12-26: I have not used `prot-simple-describe-symbol'
;; since a very long time. The idea is fine, but having a key binding
;; to provide a shortcut for C-h o RET is wasteful.
;; (autoload 'symbol-at-point "thingatpt")
;;
;; ;;;###autoload
;; (defun prot-simple-describe-symbol ()
;; "Run `describe-symbol' for the `symbol-at-point'."
;; (interactive)
;; (describe-symbol (symbol-at-point)))
;;;###autoload
(defun prot-simple-delete-window-dwim ()
"Do What I Mean to delete the current THING.
When there is more than one window, THING is a window.
When there are more than one `tab-bar-mode' tabs, THING is a tab.
Else THING is a frame if frames are more than one."
(declare (interactive-only t))
(interactive)
(cond
((length> (window-list) 1)
(delete-window))
((and (featurep 'tab-bar)
(length> (tab-bar-tabs) 1))
(tab-close))
((length> (frame-list) 1)
(delete-frame))
(t
(user-error "Nothing to delete"))))
;; DEPRECATED 2023-12-26: The `prot-simple-goto-definition' is a good
;; idea but it needs more work. Ultimately though, it is easier to
@ -154,8 +189,8 @@ The DWIM behaviour of this command is as follows:
;;
;; (defun prot-simple--number-replace (number amount operation)
;; "Perform OPERATION on NUMBER at point by AMOUNT."
;; (when-let ((bounds (bounds-of-thing-at-point 'number))
;; (replacement (prot-simple--number-operate number amount operation)))
;; (when-let* ((bounds (bounds-of-thing-at-point 'number))
;; (replacement (prot-simple--number-operate number amount operation)))
;; (delete-region (car bounds) (cdr bounds))
;; (save-excursion
;; (insert (number-to-string replacement)))))
@ -218,6 +253,38 @@ When called interactively without a prefix numeric argument, N is
(make-obsolete 'prot-simple-copy-line-or-region 'prot-simple-copy-line "2023-09-26")
;;;###autoload
(defun prot-simple-kill-ring-save (&optional beg end)
"Copy the current region or line.
When the region is active, use `kill-ring-save' between the BEG and END
positions. Otherwise, copy the current line."
;; NOTE 2025-02-23: Using (interactive "r") returns an error before
;; running the body of this function if there is no mark. This
;; happens when visiting a file.
(interactive
(when (region-active-p)
(list
(region-beginning)
(region-end))))
(if (and beg end)
(copy-region-as-kill beg end)
(prot-simple-copy-line))
(setq this-command 'kill-ring-save))
;;;###autoload
(defun prot-simple-kill-region (&optional beg end)
"Do `kill-region' when the region is active, else `kill-ring-save' symbol at point."
(interactive
(when (region-active-p)
(list
(region-beginning)
(region-end))))
(if (and beg end)
(kill-region beg end)
(prot-simple-mark-sexp)
(copy-region-as-kill (region-beginning) (region-end)))
(setq this-command 'kill-ring-save))
(defun prot-simple--duplicate-buffer-substring (boundaries)
"Duplicate buffer substring between BOUNDARIES.
BOUNDARIES is a cons cell representing buffer positions."
@ -238,7 +305,8 @@ BOUNDARIES is a cons cell representing buffer positions."
(prot-simple--duplicate-buffer-substring
(if (region-active-p)
(cons (region-beginning) (region-end))
(cons (line-beginning-position) (line-end-position)))))
(cons (line-beginning-position) (line-end-position))))
(setq this-command 'yank))
;;;###autoload
(defun prot-simple-yank-replace-line-or-region ()
@ -249,7 +317,8 @@ This command can then be followed by the standard
(if (use-region-p)
(delete-region (region-beginning) (region-end))
(delete-region (line-beginning-position) (line-end-position)))
(yank))
(yank)
(setq this-command 'yank))
;;;###autoload
(defun prot-simple-multi-line-below ()
@ -269,7 +338,75 @@ This command can then be followed by the standard
(defun prot-simple-kill-line-backward ()
"Kill from point to the beginning of the line."
(interactive)
(kill-line 0))
(kill-line 0)
(setq this-command 'kill-line))
;;;###autoload
(defun prot-simple-copy-line-forward (n)
"Copy from point to the end of the Nth line.
Without numeric prefix argument N, operate on the current line."
(interactive "p")
(let ((point (point))
(end (line-end-position n))
(max (point-max)))
(copy-region-as-kill
point
(if (> end max)
max
end)))
(setq this-command 'kill-ring-save))
;;;###autoload
(defun prot-simple-copy-line-backward (n)
"Copy from point to the beginning of the Nth line.
Without numeric prefix argument N, operate on the current line."
(interactive "p")
(let ((point (point))
(beg (line-beginning-position n))
(min (point-min)))
(copy-region-as-kill
point
(if (< beg min)
min
beg)))
(setq this-command 'kill-ring-save))
;;;###autoload
(defun prot-simple-delete-line ()
"Delete (not kill) from point to the end of the line."
(interactive)
(let* ((point (point))
(end (line-end-position))
(end+ (+ end 1)))
(cond
((> end+ (point-max)) (delete-region point end))
((= point end) (delete-region point end+))
(t (delete-region point end))))
(setq this-command 'delete-region))
;;;###autoload
(defun prot-simple-delete-line-backward ()
"Delete (not kill) from point to the beginning of the line."
(interactive)
(let* ((point (point))
(beg (line-beginning-position))
(beg- (- beg 1)))
(cond
((< beg- (point-min)) (delete-region beg point))
((= point beg) (delete-region beg- point))
(t (delete-region beg point))))
(setq this-command 'delete-region))
;;;###autoload
(define-minor-mode prot-simple-auto-fill-visual-line-mode
"Enable `visual-line-mode' and disable `auto-fill-mode' in the current buffer."
:global nil
(if prot-simple-auto-fill-visual-line-mode
(progn
(auto-fill-mode -1)
(visual-line-mode 1))
(auto-fill-mode 1)
(visual-line-mode -1)))
;;;; Commands for text insertion or manipulation
@ -311,7 +448,7 @@ to `line-beginning-position'."
(line-end-position) :no-error
(prefix-numeric-value current-prefix-arg))
(line-beginning-position))))
(when-let ((regexp-end (prot-simple--pos-url-on-line char)))
(when-let* ((regexp-end (prot-simple--pos-url-on-line char)))
(goto-char regexp-end)
(unless (looking-at ">")
(insert ">")
@ -457,9 +594,9 @@ demarcated by BEG and END."
;; When the region is active, toggle the presence of STRING for each
;; line in the region."
;; (interactive (list (prot-simple-line-prefix-infer-or-prompt)))
;; (if-let ((region-p (region-active-p))
;; (beg (region-beginning))
;; (end (line-number-at-pos (region-end))))
;; (if-let* ((region-p (region-active-p))
;; (beg (region-beginning))
;; (end (line-number-at-pos (region-end))))
;; (progn
;; (goto-char beg)
;; (push-mark (point))
@ -479,11 +616,11 @@ demarcated by BEG and END."
(end (pos-eol))
diff-eol-point
diff-eol-mark)
(when-let (((use-region-p))
(pos (point))
(mrk (mark))
(line-diff-mark-point (1+ (- (line-number-at-pos mrk)
(line-number-at-pos pos)))))
(when-let* (((use-region-p))
(pos (point))
(mrk (mark))
(line-diff-mark-point (1+ (- (line-number-at-pos mrk)
(line-number-at-pos pos)))))
(if (> pos mrk)
(setq start (pos-bol line-diff-mark-point)) ; pos-bol of where the mark is
(setq end (pos-eol line-diff-mark-point))) ; pos-eol of the line where the mark is
@ -510,15 +647,15 @@ demarcated by BEG and END."
(defun prot-simple--move-line-user-error (boundary)
"Return `user-error' with message accounting for BOUNDARY.
BOUNDARY is a buffer position, expected to be `point-min' or `point-max'."
(when-let ((bound (line-number-at-pos boundary))
(scope (cond
((and (use-region-p)
(or (= (line-number-at-pos (point)) bound)
(= (line-number-at-pos (mark)) bound)))
"region is ")
((= (line-number-at-pos (point)) bound)
"")
(t nil))))
(when-let* ((bound (line-number-at-pos boundary))
(scope (cond
((and (use-region-p)
(or (= (line-number-at-pos (point)) bound)
(= (line-number-at-pos (mark)) bound)))
"region is ")
((= (line-number-at-pos (point)) bound)
"")
(t nil))))
(user-error (format "Warning: %salready in the last line!" scope))))
(defun prot-simple-move-above-dwim (arg)
@ -567,23 +704,22 @@ There is no dragging the character forward. This is the
behaviour of `transpose-chars' when point is at the end of the
line."
(interactive)
(transpose-chars -1)
(forward-char))
(if (eq (point) (line-end-position))
(transpose-chars 1)
(transpose-chars -1)
(forward-char)))
;;;; Commands for paragraphs
;;;###autoload
(defun prot-simple-unfill-region-or-paragraph (&optional beg end)
"Unfill paragraph or, when active, the region.
Join all lines in region delimited by BEG and END, if active,
while respecting any empty lines (so multiple paragraphs are not
joined, just unfilled). If no region is active, operate on the
paragraph. The idea is to produce the opposite effect of both
`fill-paragraph' and `fill-region'."
(interactive "r")
(defun prot-simple-unfill-region-or-paragraph ()
"Unfill current paragraph or the active region."
(interactive)
(unless mark-ring ; needed when entering a new buffer
(push-mark (point) t nil))
(let ((fill-column most-positive-fixnum))
(if (use-region-p)
(fill-region beg end)
(if (region-active-p)
(fill-region (region-beginning) (region-end))
(fill-paragraph))))
;;;; Commands for windows and pages
@ -735,14 +871,14 @@ Name the buffer after the defun's symbol."
(defun prot-simple-display-unsaved-buffers ()
"Produce buffer menu listing unsaved file-visiting buffers."
(interactive)
(if-let ((unsaved-buffers (prot-simple--get-unsaved-buffers)))
(if-let* ((unsaved-buffers (prot-simple--get-unsaved-buffers)))
(prot-simple--display-unsaved-buffers unsaved-buffers "*Unsaved buffers*")
(message "No unsaved buffers")))
(defun prot-simple-display-unsaved-buffers-on-exit (&rest _)
"Produce buffer menu listing unsaved file-visiting buffers.
Add this as :before advice to `save-buffers-kill-emacs'."
(when-let ((unsaved-buffers (prot-simple--get-unsaved-buffers)))
(when-let* ((unsaved-buffers (prot-simple--get-unsaved-buffers)))
(prot-simple--display-unsaved-buffers unsaved-buffers "*Unsaved buffers*")))
;;;###autoload
@ -769,8 +905,13 @@ When called interactively, prompt for BUFFER."
(let ((kill-buffer-query-functions nil))
(kill-buffer (or buffer (current-buffer)))))
(make-obsolete
'prot-simple-kill-buffer-current
'prot-simple-kill-buffer-dwim
"2025-11-04")
;;;###autoload
(defun prot-simple-kill-buffer-current (&optional arg)
(defun prot-simple-kill-buffer-dwim (&optional arg)
"Kill current buffer.
With optional prefix ARG (\\[universal-argument]) delete the
buffer's window as well. Kill the window regardless of ARG if it
@ -778,11 +919,31 @@ satisfies `prot-common-window-small-p' and it has no previous
buffers in its history."
(interactive "P")
(let ((kill-buffer-query-functions nil))
(if (or (and (prot-common-window-small-p)
(null (window-prev-buffers)))
(and arg (not (one-window-p))))
(kill-buffer-and-window)
(kill-buffer))))
(cond
;; Is a tab whose last window's buffer is to be deleted.
((and (one-window-p)
(length> (tab-bar-tabs) 1))
(let ((buffer (current-buffer)))
(tab-close)
(kill-buffer buffer)))
;; Is an ancillary window that appeared for this buffer but is
;; otherwise not supposed to be there.
((and (prot-common-window-small-p)
(null (window-prev-buffers)))
(kill-buffer-and-window))
(t
(kill-buffer)))))
;;;###autoload
(defun prot-simple-copy-current-path ()
"Copy the current absolute path.
If in a file-visiting buffer, copy the variable `buffer-file-name'.
Else copy the `default-directory'."
(declare (interactive-only t))
(interactive)
(let ((path (or buffer-file-name default-directory)))
(kill-new path)
(message "Copied `%s'" (propertize path 'face 'success))))
;;;###autoload
(defun prot-simple-rename-file-and-buffer (name)
@ -849,38 +1010,6 @@ counter-clockwise."
(dotimes (i (- wincount 1))
(window-swap-states (elt winlist i) (elt winlist (+ i 1))))))
;;;; Commands for files
(cl-defmethod register--type ((_regval vector)) 'vector)
(cl-defmethod register-val-describe ((val vector) _verbose)
(if-let ((pos (aref val 2))
(file (aref val 1)))
(princ (format "%s at position %s" file pos))
(princ "Garbage data")))
;;;###autoload
(defun prot-simple-file-to-register (register)
"Store current location of file's point in REGISTER."
(interactive (list (register-read-with-preview "File with point to register: ")))
(set-register register (vector 'file-with-point (buffer-file-name) (point))))
(defvar prot-simple-file-to-register-jump-hook nil
"Normal hook called after jumping to a file register.
See `prot-simple-file-to-register'.")
;;;###autoload
(cl-defmethod register-val-jump-to ((val vector) delete)
"Handle how to jump to a location register.
This is like the default, but does not ask to visit a file: it does it
outright."
(cond
((eq (aref val 0) 'file-with-point)
(find-file (aref val 1))
(goto-char (aref val 2))
(run-hooks 'prot-simple-file-to-register-jump-hook))
(t (cl-call-next-method val delete))))
;;;; Commands of a general nature
(autoload 'color-rgb-to-hex "color")
@ -918,5 +1047,135 @@ VARIANT is either `dark' or `light'."
(interactive (list (prot-simple--list-accessible-colors-prompt)))
(list-colors-display (prot-simple-accessible-colors variant)))
(defun prot-simple-update-package-repositories-pull (package package-directory buffer)
"Pull PACKAGE which extends PACKAGE-DIRECTORY.
Use BUFFER for standard output and return the exit code."
(let ((default-directory package-directory))
(message "Pulling %s from directory %s" package default-directory)
(call-process "git" nil (list buffer t) nil "pull")))
(defun prot-simple-update-package-repositories-clone (package base-directory buffer)
"Clone PACKAGE to an extension of BASE-DIRECTORY.
Use BUFFER for standard output and return the exit code."
(message "Cloning %s to directory %s" package base-directory)
(call-process "git" nil (list buffer t) nil "clone" (format "git@github.com:protesilaos/%s %s" package base-directory)))
(define-error 'prot-package-no-update "Package could not be updated" 'error)
(defun prot-simple-update-package-repositories-subr (packages)
"Pull or clone all repositories of my PACKAGES."
(unless (executable-find "git")
(user-error "Cannot find git program; install it first or add it to the $PATH; aborting"))
(unless (getenv "SSH_AUTH_SOCK")
(user-error "Cannot find $SSH_AUTH_SOCK; check your SSH connection; aborting"))
(let ((stdout (get-buffer-create " *prot-simple-git-package-stdout*")))
(dolist (package packages)
(let* ((name (cond
((symbolp package) (symbol-name package))
((stringp package) package)
(t (error "The `%s' is neither a symbol nor a string" package))))
(common-directory (expand-file-name "~/Git/Projects/"))
(package-directory (expand-file-name name common-directory))
(exit-code (if (file-directory-p package-directory)
(prot-simple-update-package-repositories-pull package package-directory stdout)
(prot-simple-update-package-repositories-clone package common-directory stdout))))
(condition-case error-data
(when (> exit-code 0)
(signal 'prot-package-no-update (list (format "Package `%s' got exit code `%s'" package exit-code))))
(:success
(message "Updated `%s' repository" package))
(prot-package-no-update
;; TODO 2025-11-06: Is it safe to stash changes outright?
;; I think it is fine, but maybe there is a case where this
;; can lead to data loss?
(when (file-directory-p package-directory)
(let ((default-directory package-directory))
(unless (fboundp 'vc-git-stash)
(require 'vc-git))
(vc-git-stash (format "prot-package-update %s: %s" (format-time-string "%FT%T") package))))
(message "Tried to stash changes in package `%s' because: %s" package (cdr error-data)))
((error user-error)
(message "The package `%s' returned error data: %s" package error-data))
(quit
(message "Aborted by the user")))))))
(defvar prot-simple-update-package-repositories-prompt-history nil
"Minibuffer history of `prot-simple-update-package-repositories-prompt'.")
(defun prot-simple-update-package-repositories-prompt ()
"Prompt for packages among `prot-emacs-my-packages'."
(let ((default (car prot-simple-update-package-repositories-prompt-history)))
(completing-read-multiple
(format-prompt "Select packages" default)
prot-emacs-my-packages
nil t nil
'prot-simple-update-package-repositories-prompt-history
default)))
;;;###autoload
(defun prot-simple-update-some-or-all-of-my-package-repositories (packages &optional all-packages)
"Prompt for PACKAGES among `prot-emacs-my-packages' to pull or clone.
With a universal prefix argument for ALL-PACKAGES, do not prompt for packages and
update them all instead."
(interactive
(list
(if current-prefix-arg
prot-emacs-my-packages
(prot-simple-update-package-repositories-prompt))))
(prot-simple-update-package-repositories-subr packages))
;;;; Global minor mode to override keymaps
(defvar prot-simple-override-mode-map (make-sparse-keymap)
"keymap for `prot-simple-override-mode'.
Enable that mode to have its key bindings take effect over those of the
major mode.")
(define-minor-mode prot-simple-override-mode
"Enable the `prot-simple-override-mode-map'."
:init-value nil
:global t
:keymap prot-simple-override-mode-map)
;;;; Preview hexadecimal RGB colours
;; FIXME 2026-01-10: Why is this not working in Org tables? The
;; properties are applied but the face is not displayed.
(defun prot-simple-hex-fontify (limit)
"Fontify the # of a hexadecimal RGB colour up to LIMIT."
(when-let* ((found (re-search-forward "\\(#\\)\\([a-fA-F0-9]\\{6\\}\\)" limit t))
(color (match-string-no-properties 0))
(beg (match-beginning 1))
(end (match-end 1))
(face (list :background color :foreground color :inverse-video t)))
(add-text-properties beg end (list 'face face 'font-lock-face face 'display "######"))
found))
(defun prot-simple-hex-unfontify ()
"Fontify the # of a hexadecimal RGB colour up to LIMIT."
(save-excursion
(goto-char (point-min))
(while-let ((found (re-search-forward "\\(#\\)\\([a-fA-F0-9]\\{6\\}\\)" nil t))
(color (match-string-no-properties 0))
(beg (match-beginning 1))
(end (match-end 1))
(face (list :background color :foreground color :inverse-video t)))
(put-text-property beg end 'display "#")
found)))
(defvar prot-simple-hex-color-keywords
'((prot-simple-hex-fontify))
"Font lock keywords for `prot-simple-hex-color-mode'.")
(define-minor-mode prot-simple-hex-color-mode
"Preview hexadecimal colour values."
:global nil
:init-value nil
(if prot-simple-hex-color-mode
(font-lock-add-keywords nil prot-simple-hex-color-keywords)
(prot-simple-hex-unfontify)
(font-lock-remove-keywords nil prot-simple-hex-color-keywords))
(font-lock-flush (point-min) (point-max)))
(provide 'prot-simple)
;;; prot-simple.el ends here

View file

@ -1,8 +1,8 @@
;;; prot-spell.el --- Spelling-related extensions for my dotemacs -*- lexical-binding: t -*-
;; Copyright (C) 2021-2023 Protesilaos Stavrou
;; Copyright (C) 2021-2026 Protesilaos
;; Author: Protesilaos Stavrou <info@protesilaos.com>
;; Author: Protesilaos <info@protesilaos.com>
;; URL: https://protesilaos.com/emacs/dotemacs
;; Version: 0.1.0
;; Package-Requires: ((emacs "30.1"))
@ -26,11 +26,6 @@
;;
;; This covers my spelling-related extensions, for use in my Emacs
;; setup: https://protesilaos.com/emacs/dotemacs.
;;
;; Remember that every piece of Elisp that I write is for my own
;; educational and recreational purposes. I am not a programmer and I
;; do not recommend that you copy any of this if you are not certain of
;; what it does.
;;; Code:
@ -101,5 +96,42 @@ function. Then you can control the buffer's specifics via
(advice-add #'ispell-display-buffer :override #'prot-spell-ispell-display-buffer)
;;;; French punctuation
;; TODO 2026-07-01: Maybe extend the command to check for all sorts of
;; rules, in accordance with the Charte orthotypographique du Journal
;; officiel:
;; <https://www.legifrance.gouv.fr/contenu/Media/Files/autour-de-la-loi/legislatif-et-reglementaire/charte_typographique_jo_janvier_2021.pdf?__cf_chl_f_tk=YcOBi.sMMWmrN5FxCQMM8YDz.OyTC62mXKuZnjZ2rvs-1782901834-1.0.1.1-IzZmLEEkwksUf1sSm5dljuFqSZjDPjXfLMYkIdbOa.Y>.
;;
;; Though that is not a simple search+replace operation...
;;
;; For the following:
;;
;; «Lespace insécable permet déviter la séparation entre deux
;; éléments. Elle sutilisera, entre autres, à lintérieur des
;; guillemets français, au sein des nombres, après les dito, avec les
;; tirets dincise.»
(defvar prot-spell-french-grammar-replacements
'(("\\([^[:punct:][:space:]]\\)\\?" . "\\1 ?")
("\\([^[:punct:][:space:]]\\):" . "\\1 :")
("\\([^[:punct:][:space:]]\\);" . "\\1 ;")
("\\([^[:punct:][:space:]]\\)!" . "\\1 !")
("«\\([^[:punct:][:space:]]\\)" . "« \\1")
("\\([^[:punct:][:space:]]\\" . "\\1 »")
("\\([^[:punct:][:space:]]\\)%" . "\\1 %"))
"Add no-break space to relevant punctuation.")
;;;###autoload
(defun prot-spell-french-punctuation ()
"Apply the `prot-spell-french-grammar-replacements' across the buffer."
(interactive)
(save-excursion
(save-restriction
(widen)
(pcase-dolist (`(,target . ,replacement) prot-spell-french-grammar-replacements)
(goto-char (point-min))
(while (re-search-forward target nil t)
(replace-match replacement))))))
(provide 'prot-spell)
;;; prot-spell.el ends here

View file

@ -0,0 +1,80 @@
;;; prot-streaming.el --- Tweaks for when I do videos or share my screen -*- lexical-binding: t -*-
;; Copyright (C) 2026 Protesilaos
;; Author: Protesilaos <info@protesilaos.com>
;; URL: https://protesilaos.com/emacs/dotemacs
;; Version: 0.1.0
;; Package-Requires: ((emacs "30.1"))
;; This file is NOT part of GNU Emacs.
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;;
;; This covers my streaming-related extensions, for use in my Emacs
;; setup: https://protesilaos.com/emacs/dotemacs.
;;; Code:
(require 'display-line-numbers)
(require 'hl-line)
(defcustom prot-streaming-mode-modes
'(text-mode prog-mode dired-mode comint-mode)
"Major modes where `prot-streaming-mode' takes effect.")
(defun prot-streaming-mode--display-line-numbers (arg)
"Call `display-line-numbers-mode' with ARG.
Do it for all buffers whose major mode is one among `prot-streaming-mode-modes'."
(dolist (buffer (buffer-list))
(with-current-buffer buffer
(when (derived-mode-p prot-streaming-mode-modes)
(display-line-numbers-mode arg)))))
(defun prot-streaming--get-mode-hooks ()
"Return hooks belonging to `prot-streaming-mode-modes'."
(mapcar
(lambda (symbol)
(intern (format "%s-hook" symbol)))
prot-streaming-mode-modes))
(defun prot-streaming-mode--affect-hooks (add-or-remove)
"Apply ADD-OR-REMOVE to `prot-streaming-mode-hooks'.
ADD-OR-REMOVE is the keyword `:add' or `:remove', which correspond to
`add-hook' and `remove-hook'."
(let ((fn (pcase add-or-remove
(:add #'add-hook)
(:remove #'remove-hook)
(_ (error "ADD-OR-REMOVE must be either `:add' or `:remove'")))))
(dolist (hook (prot-streaming--get-mode-hooks))
(funcall fn hook #'display-line-numbers-mode))))
;;;###autoload
(define-minor-mode prot-streaming-mode
"Enable extras for streaming."
:global t
:init-value nil
(if prot-streaming-mode
(progn
(prot-streaming-mode--display-line-numbers 1)
(prot-streaming-mode--affect-hooks :add)
(global-hl-line-mode 1))
(prot-streaming-mode--display-line-numbers -1)
(prot-streaming-mode--affect-hooks :remove)
(global-hl-line-mode -1)))
(provide 'prot-streaming)
;;; prot-streaming.el ends here

View file

@ -1,8 +1,8 @@
;;; prot-vertico.el --- Custom Vertico extras -*- lexical-binding: t -*-
;; Copyright (C) 2023 Protesilaos Stavrou
;; Copyright (C) 2023-2026 Protesilaos
;; Author: Protesilaos Stavrou <info@protesilaos.com>
;; Author: Protesilaos <info@protesilaos.com>
;; URL: https://protesilaos.com/emacs/dotemacs
;; Version: 0.1.0
;; Package-Requires: ((emacs "30.1"))
@ -23,11 +23,6 @@
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;;
;; Remember that every piece of Elisp that I write is for my own
;; educational and recreational purposes. I am not a programmer and I
;; do not recommend that you copy any of this if you are not certain of
;; what it does.
;;; Code:
@ -40,7 +35,8 @@
:prompt ""
:separator ""
:ellipsis ""
:no-match "")))
:no-match ""))
(vertico-preselect . prompt))
"List of configurations for minimal Vertico multiform.
The minimal view is intended to be more private or less
revealing. This is important when, for example, a prompt shows
@ -53,38 +49,29 @@ command or use the commands `prot-vertico-private-next' and
automatically.")
(defvar prot-vertico-multiform-maximal
'((vertico-count . 10)
'(reverse
(vertico-count . 10)
(vertico-preselect . directory)
(vertico-resize . t))
"List of configurations for maximal Vertico multiform.")
(defun prot-vertico--match-directory (str)
"Match directory delimiter in STR."
(string-suffix-p "/" str))
;; From the Vertico documentation.
(defun prot-vertico-sort-directories-first (files)
"Sort directories before FILES."
(setq files (vertico-sort-alpha files))
(nconc (seq-filter #'prot-vertico--match-directory files)
(seq-remove #'prot-vertico--match-directory files)))
(defun prot-vertico-private-next ()
"Like `vertico-next' but toggle vertical view if needed.
This is done to accommodate `prot-vertico-multiform-minimal'."
(interactive)
(if vertico-unobtrusive-mode
(let ((vertico--index 0))
(vertico-multiform-vertical)
(vertico-next 1))
(vertico-next 1)))
(defun prot-vertico-private-previous ()
"Like `vertico-previous' but toggle vertical view if needed.
"Like `vertico-next' but toggle reverse vertical view if needed.
This is done to accommodate `prot-vertico-multiform-minimal'."
(interactive)
(if vertico-unobtrusive-mode
(progn
(vertico-multiform-vertical)
(vertico-multiform-reverse)
(vertico-next 1))
(vertico-next 1)))
(defun prot-vertico-private-previous ()
"Like `vertico-previous' but toggle reverse vertical view if needed.
This is done to accommodate `prot-vertico-multiform-minimal'."
(interactive)
(if vertico-unobtrusive-mode
(progn
(vertico-multiform-reverse)
(vertico-previous 1))
(vertico-previous 1)))
@ -95,8 +82,43 @@ This is done to accommodate `prot-vertico-multiform-minimal'."
(if (and vertico-unobtrusive-mode (> vertico--total 1))
(progn
(minibuffer-complete)
(vertico-multiform-vertical))
(prot-vertico-private-next))
(vertico-insert)))
(defun prot-vertico-private-exit ()
"Exit with the candidate if `prot-vertico-multiform-minimal'.
If there are more candidates that match the given input, expand the
minibuffer to show the remaining candidates and select the first one.
Else do `vertico-exit'."
(interactive)
(cond
((and (= vertico--total 1)
(not (eq 'file (vertico--metadata-get 'category))))
(minibuffer-complete)
(vertico-exit))
((and vertico-unobtrusive-mode
(not minibuffer--require-match)
(or (string-empty-p (minibuffer-contents))
minibuffer-default
(eq vertico-preselect 'directory)
(eq vertico-preselect 'prompt)))
(vertico-exit-input))
((and vertico-unobtrusive-mode (> vertico--total 1))
(minibuffer-complete-and-exit)
(prot-vertico-private-next))
(t
(vertico-exit))))
;; (cl-defgeneric vertico--display-candidates (lines)
;; "Reverse the default vertico view of LINES."
;; (move-overlay vertico--candidates-ov (point-min) (point-min))
;; ;; (setq lines (nreverse lines))
;; (unless (eq vertico-resize t)
;; (setq lines (nconc (make-list (max 0 (- vertico-count (length lines))) "\n") lines)))
;; (let ((string (apply #'concat lines)))
;; (add-face-text-property 0 (length string) 'default 'append string)
;; (overlay-put vertico--candidates-ov 'before-string string)
;; (overlay-put vertico--candidates-ov 'after-string nil)))
(provide 'prot-vertico)
;;; prot-vertico.el ends here

View file

@ -1,8 +1,8 @@
;;; prot-window.el --- Display-buffer and window-related extensions for my dotemacs -*- lexical-binding: t -*-
;; Copyright (C) 2023 Protesilaos Stavrou
;; Copyright (C) 2023-2026 Protesilaos
;; Author: Protesilaos Stavrou <info@protesilaos.com>
;; Author: Protesilaos <info@protesilaos.com>
;; URL: https://protesilaos.com/emacs/dotemacs
;; Version: 0.1.0
;; Package-Requires: ((emacs "30.1"))
@ -26,11 +26,6 @@
;;
;; This covers my window and display-buffer extensions, for use in my
;; Emacs setup: https://protesilaos.com/emacs/dotemacs.
;;
;; Remember that every piece of Elisp that I write is for my own
;; educational and recreational purposes. I am not a programmer and I
;; do not recommend that you copy any of this if you are not certain of
;; what it does.
;;; Code:
@ -48,7 +43,7 @@ number (integer or floating point) or a function.")
(defun prot-window--get-window-size (key)
"Extract the value of KEY from `prot-window-window-sizes'."
(when-let ((value (plist-get prot-window-window-sizes key)))
(when-let* ((value (plist-get prot-window-window-sizes key)))
(cond
((functionp value)
(funcall value))
@ -101,6 +96,21 @@ This as the action function in a `display-buffer-alist' entry."
(when (apply fn args)
(throw 'success fn))))))
(defun prot-window-display-new (buffer alist)
"Display buffer in a new window if possible."
(when-let* ((root (frame-root-window))
(window (ignore-errors
(or (split-window-horizontally nil root)
(split-window-below nil root)))))
(window--display-buffer buffer window 'window alist)))
(defun prot-window-display-other-or-current (&rest args)
"Display buffer in the current or other window.
Use the other window if only one window is present."
(if (length> (window-list) 1)
(apply #'display-buffer-same-window args)
(apply #'display-buffer-pop-up-window args)))
(defun prot-window-shell-or-term-p (buffer &rest _)
"Check if BUFFER is a shell or terminal.
This is a predicate function for `buffer-match-p', intended for
@ -148,9 +158,9 @@ call NAME as a function."
(defun prot-window--get-new-shell-buffer ()
"Return buffer name for `shell' buffers."
(if-let ((buffers (prot-window--get-shell-buffers))
(buffers-length (length buffers))
((>= buffers-length 1)))
(if-let* ((buffers (prot-window--get-shell-buffers))
(buffers-length (length buffers))
((>= buffers-length 1)))
(format "*shell*<%s>" (1+ buffers-length))
"*shell*"))
@ -159,7 +169,7 @@ call NAME as a function."
(let ((name (prot-window--get-new-shell-buffer)))
(shell name)
(set-frame-name name)
(when-let ((buffer (get-buffer name)))
(when-let* ((buffer (get-buffer name)))
(with-current-buffer buffer
(add-hook
'delete-frame-functions
@ -173,13 +183,16 @@ call NAME as a function."
nil
:local)))))
;;;###autoload (autoload 'prot-window-coach "prot-window")
(prot-window-define-full-frame coach
(let ((buffer (get-buffer-create "*scratch for coach*")))
;;;###autoload (autoload 'prot-window-meeting "prot-window")
(prot-window-define-full-frame meeting
(let ((buffer (get-buffer-create "*scratch for meeting*")))
(with-current-buffer buffer
(funcall initial-major-mode))
(funcall initial-major-mode)
(when (and (zerop (buffer-size))
(stringp initial-scratch-message))
(insert initial-scratch-message)))
(display-buffer buffer)
(set-frame-name "Coach")))
(set-frame-name "Meeting")))
;; REVIEW 2023-06-25: Does this merit a user option? I don't think I
;; will ever set it to the left. It feels awkward there.
@ -197,5 +210,59 @@ call NAME as a function."
(add-hook 'after-make-frame-functions 'prot-window-no-minibuffer-scroll-bar)
;;;; Run commands in a popup frame (via emacsclient)
(defun prot-window-delete-popup-frame (&rest _)
"Kill selected selected frame if it has parameter `prot-window-popup-frame'.
Use this function via a hook."
(dolist (frame (frame-list))
(when (frame-parameter frame 'prot-window-popup-frame)
(delete-frame frame))))
;; NOTE 2025-02-11: Also see `prot-vertico-with-buffer-mode', which
;; extends this to use a full-frame buffer for Vertico.
(defmacro prot-window-define-with-popup-frame (command)
"Define function which calls COMMAND in a new frame.
Make the new frame have the `prot-window-popup-frame' parameter."
`(defun ,(intern (format "prot-window-popup-%s" command)) ()
,(format "Run `%s' in a popup frame with `prot-window-popup-frame' parameter.
Also see `prot-window-delete-popup-frame'." command)
(interactive)
(let ((frame (make-frame '((prot-window-popup-frame . t)
(explicit-name . t)
(name . "prot-window-popup")))))
(select-frame frame)
(switch-to-buffer " prot-window-hidden-buffer-for-popup-frame")
(condition-case nil
(call-interactively ',command)
((quit error user-error)
(delete-frame frame))))))
(declare-function org-capture "org-capture" (&optional goto keys))
(declare-function tmr "tmr" (time &optional description acknowledgep))
(declare-function prot-project-switch "prot-project" (directory))
;;;###autoload (autoload 'prot-window-popup-org-capture "prot-window")
(prot-window-define-with-popup-frame org-capture) ; defines command `prot-window-popup-org-capture'
;;;###autoload (autoload 'prot-window-popup-tmr "prot-window")
(prot-window-define-with-popup-frame tmr) ; defines command `prot-window-popup-tmr'
;;;###autoload (autoload 'prot-window-popup-prot-project-switch "prot-window")
(prot-window-define-with-popup-frame prot-project-switch) ; defines command `prot-window-popup-prot-project-switch'
(defun prot-window-set-delete-popup-hook (feature hook)
"Set up `prot-window-delete-popup-frame' for FEATURE with HOOK."
(with-eval-after-load feature
(add-hook hook #'prot-window-delete-popup-frame)))
(defvar org-capture-after-finalize-hook)
(defvar tmr-timer-created-functions)
(defvar prot-project-switch-hook)
(prot-window-set-delete-popup-hook 'org-capture 'org-capture-after-finalize-hook)
(prot-window-set-delete-popup-hook 'tmr 'tmr-timer-created-functions)
(prot-window-set-delete-popup-hook 'prot-project 'prot-project-switch-hook)
(provide 'prot-window)
;;; prot-window.el ends here

View file

@ -0,0 +1,21 @@
[Desktop Entry]
Name=Prot Emacsclient
GenericName=Text Editor
Comment=Edit text
MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;x-scheme-handler/org-protocol;
Exec=sh -c "if [ -n \\"\\$*\\" ]; then exec /usr/local/bin/emacsclient --alternate-editor="emacs" --display=\\"\\$DISPLAY\\" \\"\\$@\\"; else exec /usr/local/bin/emacsclient --alternate-editor="emacs" --create-frame; fi" sh %F
Icon=emacs
Type=Application
Terminal=false
Categories=Development;TextEditor;
StartupWMClass=Emacs
Keywords=emacsclient;
Actions=new-window;new-instance;
[Desktop Action new-window]
Name=New Window
Exec=/usr/local/bin/emacsclient --alternate-editor="emacs" --create-frame %F
[Desktop Action new-instance]
Name=New Instance
Exec=emacs %F

View file

@ -7,11 +7,11 @@
<description>Set substitute fonts for Latin</description>
<!-- Iosevka Comfy is my custom build of Iosevka: <https://git.sr.ht/~protesilaos/iosevka-comfy> -->
<!-- Aporetic is my custom build of Iosevka: <https://github.com/protesilaos/aporetic> -->
<!-- Serif -->
<alias>
<family>Iosevka Comfy Motion Duo</family>
<family>Aporetic Serif</family>
<default><family>serif</family></default>
</alias>
<alias>
@ -29,7 +29,7 @@
<!-- Sans Serif -->
<alias>
<family>Iosevka Comfy Duo</family>
<family>Aporetic Sans</family>
<default><family>sans-serif</family></default>
</alias>
<alias>
@ -47,8 +47,7 @@
<!-- Monospaced -->
<alias>
<!-- We need the "fixed" variant for programs such as XTerm -->
<family>Iosevka Comfy Motion Fixed</family>
<family>Aporetic Serif Mono</family>
<default><family>monospace</family></default>
</alias>
<alias>

View file

@ -7,12 +7,12 @@
<description>Set preferable fonts for Latin</description>
<!-- Iosevka Comfy is my custom build of Iosevka: <https://git.sr.ht/~protesilaos/iosevka-comfy> -->
<!-- Aporetic is my custom build of Iosevka: <https://github.com/protesilaos/aporetic> -->
<alias binding="strong">
<family>serif</family>
<prefer>
<family>Iosevka Comfy Motion Duo</family>
<family>Aporetic Serif</family>
<family>Noto Serif</family>
<family>Liberation Serif</family>
</prefer>
@ -23,7 +23,7 @@
<alias binding="strong">
<family>sans-serif</family>
<prefer>
<family>Iosevka Comfy Duo</family>
<family>Aporetic Sans</family>
<family>FiraGO</family>
<family>Roboto</family>
<family>Noto Sans</family>
@ -36,8 +36,7 @@
<alias binding="strong">
<family>monospace</family>
<prefer>
<family>Iosevka Comfy Motion Fixed</family>
<!-- My custom build of Hack: <https://git.sr.ht/~protesilaos/hack-font-mod> -->
<family>Aporetic Serif Mono</family>
<family>Hack</family>
<family>Source Code Pro</family>
</prefer>

View file

@ -0,0 +1,159 @@
#!/bin/bash
# gnome_custom_keys.sh --- My custom key bindings for GNOME
#
# Copyright (C) 2025-2026 Protesilaos <info@protesilaos.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
### Commentary:
#
# My custom key bindings for GNOME. This may not be complete but it
# fixes all the known keybinding-related conflicts I encounter
# whenever I want to use Emacs inside of GNOME.
#
# Part of my dotfiles: <https://github.com/protesilaos/dotfiles>.
### Code:
gsettings set org.gnome.TextEditor keybindings 'default'
gsettings set org.gnome.desktop.wm.keybindings activate-window-menu "['']"
gsettings set org.gnome.desktop.wm.keybindings always-on-top "['']"
gsettings set org.gnome.desktop.wm.keybindings begin-move "['']"
gsettings set org.gnome.desktop.wm.keybindings begin-resize "['']"
gsettings set org.gnome.desktop.wm.keybindings close "['<Super>q']"
gsettings set org.gnome.desktop.wm.keybindings cycle-group "['']"
gsettings set org.gnome.desktop.wm.keybindings cycle-group-backward "['']"
gsettings set org.gnome.desktop.wm.keybindings cycle-panels "['']"
gsettings set org.gnome.desktop.wm.keybindings cycle-panels-backward "['']"
gsettings set org.gnome.desktop.wm.keybindings cycle-windows "['']"
gsettings set org.gnome.desktop.wm.keybindings cycle-windows-backward "['']"
gsettings set org.gnome.desktop.wm.keybindings lower "['']"
gsettings set org.gnome.desktop.wm.keybindings maximize "['']"
gsettings set org.gnome.desktop.wm.keybindings maximize-horizontally "['']"
gsettings set org.gnome.desktop.wm.keybindings maximize-vertically "['']"
gsettings set org.gnome.desktop.wm.keybindings minimize "['<Super>h']"
gsettings set org.gnome.desktop.wm.keybindings move-to-center "['']"
gsettings set org.gnome.desktop.wm.keybindings move-to-corner-ne "['']"
gsettings set org.gnome.desktop.wm.keybindings move-to-corner-nw "['']"
gsettings set org.gnome.desktop.wm.keybindings move-to-corner-se "['']"
gsettings set org.gnome.desktop.wm.keybindings move-to-corner-sw "['']"
gsettings set org.gnome.desktop.wm.keybindings move-to-monitor-down "['']"
gsettings set org.gnome.desktop.wm.keybindings move-to-monitor-left "['']"
gsettings set org.gnome.desktop.wm.keybindings move-to-monitor-right "['']"
gsettings set org.gnome.desktop.wm.keybindings move-to-monitor-up "['']"
gsettings set org.gnome.desktop.wm.keybindings move-to-side-e "['']"
gsettings set org.gnome.desktop.wm.keybindings move-to-side-n "['']"
gsettings set org.gnome.desktop.wm.keybindings move-to-side-s "['']"
gsettings set org.gnome.desktop.wm.keybindings move-to-side-w "['']"
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-1 "['']"
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-10 "['']"
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-11 "['']"
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-12 "['']"
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-2 "['']"
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-3 "['']"
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-4 "['']"
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-5 "['']"
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-6 "['']"
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-7 "['']"
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-8 "['']"
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-9 "['']"
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-down "['']"
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-last "['']"
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-left "['<Shift><Super>Left']"
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-right "['<Shift><Super>Right']"
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-up "['']"
gsettings set org.gnome.desktop.wm.keybindings panel-main-menu "['']"
gsettings set org.gnome.desktop.wm.keybindings panel-run-dialog "['']"
gsettings set org.gnome.desktop.wm.keybindings raise "['']"
gsettings set org.gnome.desktop.wm.keybindings raise-or-lower "['']"
gsettings set org.gnome.desktop.wm.keybindings set-spew-mark "['']"
gsettings set org.gnome.desktop.wm.keybindings show-desktop "['']"
gsettings set org.gnome.desktop.wm.keybindings switch-applications "['<Super>Tab']"
gsettings set org.gnome.desktop.wm.keybindings switch-applications-backward "['<Shift><Super>Tab']"
gsettings set org.gnome.desktop.wm.keybindings switch-group "['<Super>grave']"
gsettings set org.gnome.desktop.wm.keybindings switch-group-backward "['<Shift><Super>grave']"
gsettings set org.gnome.desktop.wm.keybindings switch-input-source "['<Super>space', 'XF86Keyboard']"
gsettings set org.gnome.desktop.wm.keybindings switch-input-source-backward "['<Shift><Super>space', '<Shift>XF86Keyboard']"
gsettings set org.gnome.desktop.wm.keybindings switch-panels "['']"
gsettings set org.gnome.desktop.wm.keybindings switch-panels-backward "['']"
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-1 "['']"
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-10 "['']"
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-11 "['']"
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-12 "['']"
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-2 "['']"
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-3 "['']"
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-4 "['']"
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-5 "['']"
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-6 "['']"
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-7 "['']"
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-8 "['']"
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-9 "['']"
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-down "['']"
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-last "['']"
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-left "['']"
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-right "['']"
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-up "['']"
gsettings set org.gnome.desktop.wm.keybindings switch-windows "['<Alt>Tab']"
gsettings set org.gnome.desktop.wm.keybindings switch-windows-backward "['<Shift><Alt>Tab']"
gsettings set org.gnome.desktop.wm.keybindings toggle-above "['']"
gsettings set org.gnome.desktop.wm.keybindings toggle-fullscreen "['<Super>f']"
gsettings set org.gnome.desktop.wm.keybindings toggle-maximized "['<Super>m']"
gsettings set org.gnome.desktop.wm.keybindings toggle-on-all-workspaces "['']"
gsettings set org.gnome.desktop.wm.keybindings toggle-shaded "['']"
gsettings set org.gnome.desktop.wm.keybindings unmaximize "['']"
gsettings set org.gnome.metacity.keybindings toggle-tiled-left "['<Super>Left']"
gsettings set org.gnome.metacity.keybindings toggle-tiled-right "['<Super>Right']"
gsettings set org.gnome.mutter.keybindings rotate-monitor "['']"
gsettings set org.gnome.mutter.keybindings switch-monitor "['']"
gsettings set org.gnome.mutter.keybindings tab-popup-cancel "['']"
gsettings set org.gnome.mutter.keybindings tab-popup-select "['']"
gsettings set org.gnome.mutter.keybindings toggle-tiled-left "['<Super>Left']"
gsettings set org.gnome.mutter.keybindings toggle-tiled-right "['<Super>Right']"
gsettings set org.gnome.mutter.wayland.keybindings restore-shortcuts "['']"
gsettings set org.gnome.mutter.wayland.keybindings switch-to-session-1 "['']"
gsettings set org.gnome.mutter.wayland.keybindings switch-to-session-10 "['']"
gsettings set org.gnome.mutter.wayland.keybindings switch-to-session-11 "['']"
gsettings set org.gnome.mutter.wayland.keybindings switch-to-session-12 "['']"
gsettings set org.gnome.mutter.wayland.keybindings switch-to-session-2 "['']"
gsettings set org.gnome.mutter.wayland.keybindings switch-to-session-3 "['']"
gsettings set org.gnome.mutter.wayland.keybindings switch-to-session-4 "['']"
gsettings set org.gnome.mutter.wayland.keybindings switch-to-session-5 "['']"
gsettings set org.gnome.mutter.wayland.keybindings switch-to-session-6 "['']"
gsettings set org.gnome.mutter.wayland.keybindings switch-to-session-7 "['']"
gsettings set org.gnome.mutter.wayland.keybindings switch-to-session-8 "['']"
gsettings set org.gnome.mutter.wayland.keybindings switch-to-session-9 "['']"
gsettings set org.gnome.settings-daemon.plugins.media-keys custom-keybindings "['/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom1/']"
gsettings set org.gnome.shell.keybindings focus-active-notification "['']"
gsettings set org.gnome.shell.keybindings open-application-menu "['']"
gsettings set org.gnome.shell.keybindings screenshot "['<Shift>Print']"
gsettings set org.gnome.shell.keybindings screenshot-window "['<Alt>Print']"
gsettings set org.gnome.shell.keybindings shift-overview-down "['']"
gsettings set org.gnome.shell.keybindings shift-overview-up "['']"
gsettings set org.gnome.shell.keybindings show-screen-recording-ui "['']"
gsettings set org.gnome.shell.keybindings show-screenshot-ui "['Print']"
gsettings set org.gnome.shell.keybindings switch-to-application-1 "['']"
gsettings set org.gnome.shell.keybindings switch-to-application-2 "['']"
gsettings set org.gnome.shell.keybindings switch-to-application-3 "['']"
gsettings set org.gnome.shell.keybindings switch-to-application-4 "['']"
gsettings set org.gnome.shell.keybindings switch-to-application-5 "['']"
gsettings set org.gnome.shell.keybindings switch-to-application-6 "['']"
gsettings set org.gnome.shell.keybindings switch-to-application-7 "['']"
gsettings set org.gnome.shell.keybindings switch-to-application-8 "['']"
gsettings set org.gnome.shell.keybindings switch-to-application-9 "['']"
gsettings set org.gnome.shell.keybindings toggle-application-view "['<Super>a']"
gsettings set org.gnome.shell.keybindings toggle-message-tray "['']"
gsettings set org.gnome.shell.keybindings toggle-overview "['<Super>d']"
gsettings set org.gtk.Settings.Debug enable-inspector-keybinding false
gsettings set org.gtk.gtk4.Settings.Debug enable-inspector-keybinding true

View file

@ -0,0 +1,3 @@
Use this:
systemctl --user enable dirmngr.socket gpg-agent.socket gpg-agent-ssh.socket

View file

@ -0,0 +1,3 @@
VteTerminal, vte-terminal {
padding: 15px 30px;
}

View file

@ -0,0 +1,3 @@
VteTerminal, vte-terminal {
padding: 15px 30px;
}

View file

@ -0,0 +1,3 @@
To enable mbsync with notmuch integration, do the following:
systemctl --user enable mbsync.timer

View file

@ -3,7 +3,7 @@ Description=Mailbox synchronization timer
[Timer]
OnBootSec=2m
OnUnitActiveSec=5m
OnUnitActiveSec=2m
Unit=mbsync.service
[Install]

View file

@ -0,0 +1,37 @@
#!/bin/bash
# mbsync_remove_uid.sh --- Remove the UID from email files.
#
# Copyright (C) 2025-2026 Protesilaos <info@protesilaos.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
### Commentary:
#
# Remove the mbsync UID from email files. THIS IS RISKY SO PLEASE MAKE
# SURE YOU UNDERSTAND WHAT YOU ARE DOING.
#
# Part of my dotfiles: <https://github.com/protesilaos/dotfiles>.
### Code:
my_mail_directory="$HOME"/.mail
while IFS= read -r -d $'\0' file
do
if [ -f "$file" ]
then
mv "$file" "${file//,U=[0-9]*:/:}"
fi
done < <(find "$my_mail_directory" -type f -iname "*,U=[0-9]*" -print0)

View file

@ -1,82 +1,89 @@
IMAPAccount pub
# Gandi
####################################################################################################
IMAPAccount gandi
Host mail.gandi.net
UserCmd "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/pub/ { print $(NF-2); exit; }'"
PassCmd "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/pub/ { print $NF; exit; }'"
SSLType IMAPS
UserCmd "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/prot-gandi/ { print $(NF-2); exit; }'"
PassCmd "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/prot-gandi/ { print $NF; exit; }'"
TLSType IMAPS
IMAPStore pub-remote
Account pub
IMAPStore gandi-remote
Account gandi
MaildirStore pub-local
MaildirStore gandi-local
Subfolders Verbatim
# The trailing "/" is important
Path ~/.mail/pub/
Inbox ~/.mail/pub/Inbox
# The trailing "/" in 'Path' is important. The path given to 'Path'
# must exist, but the path given to 'Inbox' is created automatically
# by 'mbsync'.
Path ~/.mail/gandi/
Inbox ~/.mail/gandi/Inbox
Channel pub
Far :pub-remote:
Near :pub-local:
Channel gandi
Far :gandi-remote:
Near :gandi-local:
# Include everything
Patterns *
Sync All
Create Both
Create Near
Remove Both
Expunge Both
SyncState *
##########
# NOTE 2024-12-01: I do not use the others, but have tested them in
# the past. I keep the settings here in case I decide to test the
# functionality again.
IMAPAccount inf
Host mail.gandi.net
UserCmd "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/inf/ { print $(NF-2); exit; }'"
PassCmd "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/inf/ { print $NF; exit; }'"
SSLType IMAPS
IMAPStore inf-remote
Account inf
MaildirStore inf-local
Subfolders Verbatim
# The trailing "/" is important
Path ~/.mail/inf/
Inbox ~/.mail/inf/Inbox
Channel inf
Far :inf-remote:
Near :inf-local:
# Include everything
Pattern *
Sync All
Create Both
Remove Both
Expunge Both
SyncState *
##########
IMAPAccount prv
Host mail.gandi.net
UserCmd "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/prv/ { print $(NF-2); exit; }'"
PassCmd "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/prv/ { print $NF; exit; }'"
SSLType IMAPS
IMAPStore prv-remote
Account prv
MaildirStore prv-local
Subfolders Verbatim
# The trailing "/" is important
Path ~/.mail/prv/
Inbox ~/.mail/prv/Inbox
Channel prv
Far :prv-remote:
Near :prv-local:
# Include everything
Patterns *
Sync All
Create Both
Remove Both
Expunge Both
SyncState *
# # Gmail
# ####################################################################################################
# IMAPAccount gmail
# Host imap.gmail.com
# UserCmd "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/gmail/ { print $(NF-2); exit; }'"
# PassCmd "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/gmail/ { print $NF; exit; }'"
# SSLType IMAPS
#
# IMAPStore gmail-remote
# Account gmail
#
# MaildirStore gmail-local
# Subfolders Verbatim
# # The trailing "/" is important
# Path ~/.mail/gmail/
# Inbox ~/.mail/gmail/Inbox
#
# Channel gmail
# Far :gmail-remote:
# Near :gmail-local:
# # Include everything
# Patterns *
# Sync All
# Create Near
# Remove Near
# Expunge Near
# SyncState *
# # Mailbox
# ####################################################################################################
# IMAPAccount mailbox
# Host imap.mailbox.org
# UserCmd "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/mailbox/ { print $(NF-2); exit; }'"
# PassCmd "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/mailbox/ { print $NF; exit; }'"
# SSLType IMAPS
#
# IMAPStore mailbox-remote
# Account mailbox
#
# MaildirStore mailbox-local
# Subfolders Verbatim
# # The trailing "/" is important
# Path ~/.mail/mailbox/
# Inbox ~/.mail/mailbox/Inbox
#
# Channel mailbox
# Far :mailbox-remote:
# Near :mailbox-local:
# # Include everything
# Patterns *
# Sync All
# Create Near
# Remove Near
# Expunge Near
# SyncState *

133
mpv/.config/mpv/input.conf Normal file
View file

@ -0,0 +1,133 @@
MBTN_LEFT ignore
MBTN_LEFT_DBL ignore
MBTN_RIGHT ignore
MBTN_BACK ignore
MBTN_FORWARD ignore
WHEEL_UP ignore
WHEEL_DOWN ignore
WHEEL_LEFT ignore
WHEEL_RIGHT ignore
RIGHT seek 5 # seek 5 seconds forward
LEFT seek -5 # seek 5 seconds backward
UP seek 60 # seek 1 minute forward
DOWN seek -60 # seek 1 minute backward
Shift+RIGHT ignore
Shift+LEFT ignore
Shift+UP ignore
Shift+DOWN ignore
Ctrl+LEFT ignore
Ctrl+RIGHT ignore
Ctrl+Shift+LEFT ignore
Ctrl+Shift+RIGHT ignore
Alt+left ignore
Alt+right ignore
Alt+up ignore
Alt+down ignore
Alt++ ignore
Alt+- ignore
Alt+BS ignore
PGUP ignore
PGDWN ignore
Shift+PGUP ignore
Shift+PGDWN ignore
[ ignore
] ignore
{ ignore
} ignore
BS ignore
Shift+BS ignore
Shift+Ctrl+BS ignore
q ignore
Q ignore
q ignore
ESC ignore
ESC ignore
p ignore
. ignore
, ignore
SPACE cycle pause # toggle pause/playback mode
> ignore
ENTER ignore
< ignore
O ignore
o ignore
P ignore
i ignore
I ignore
` ignore
z ignore
Z ignore
x ignore
ctrl++ ignore
ctrl+- ignore
Shift+g ignore
Shift+f ignore
9 ignore
/ ignore
0 ignore
* ignore
m ignore
1 ignore
2 ignore
3 ignore
4 ignore
5 ignore
6 ignore
7 ignore
8 ignore
Alt+0 ignore
Alt+1 ignore
Alt+2 ignore
d ignore
r ignore
R ignore
t ignore
v ignore
Alt+v ignore
V ignore
u ignore
j ignore
J ignore
SHARP ignore
_ ignore
T ignore
f ignore
s ignore
S ignore
Ctrl+s ignore
Alt+s ignore
w ignore
W ignore
e ignore
A ignore
POWER ignore
PLAY ignore
PAUSE ignore
PLAYPAUSE ignore
PLAYONLY ignore
PAUSEONLY ignore
STOP ignore
FORWARD ignore
REWIND ignore
NEXT ignore
PREV ignore
VOLUME_UP ignore
VOLUME_DOWN ignore
MUTE ignore
CLOSE_WIN quit
ctrl+w ignore
E ignore
l ignore
L ignore
ctrl+c ignore
DEL ignore
ctrl+h ignore
F8 ignore
F9 ignore
! ignore
@ ignore
? ignore
? ignore

View file

@ -1,36 +1,43 @@
# See my mbsync config, which is reflectd here.
# See my mbsync config, which is reflected here.
# https://github.com/protesilaos/dotfiles
defaults
auth on
protocol smtp
tls on
# FIXME 2023-08-08: I cannot send the message if `tls_starttls' is on.
# Why?
tls_starttls off
tls_starttls on
# Gandi
####################################################################################################
account gandi
host mail.gandi.net
port 465
port 587
eval echo from $(gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/prot-gandi/ { print $(NF-2); exit; }')
eval echo user $(gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/prot-gandi/ { print $(NF-2); exit; }')
passwordeval "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/prot-gandi/ { print $NF; exit; }'"
# pub
account pub
# FIXME 2023-08-08: Optimise this?
eval echo from $(gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/pub/ { print $(NF-2); exit; }')
eval echo user $(gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/pub/ { print $(NF-2); exit; }')
passwordeval "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/pub/ { print $NF; exit; }'"
# NOTE 2024-12-01: I do not use the others, but have tested them in
# the past. I keep the settings here in case I decide to test the
# functionality again.
# inf
account inf
# FIXME 2023-08-08: Optimise this?
eval echo from $(gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/inf/ { print $(NF-2); exit; }')
eval echo user $(gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/inf/ { print $(NF-2); exit; }')
passwordeval "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/inf/ { print $NF; exit; }'"
# # Gmail
# ####################################################################################################
# account gmail
# host smtp.gmail.com
# port 587
# eval echo from $(gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/gmail/ { print $(NF-2); exit; }')
# eval echo user $(gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/gmail/ { print $(NF-2); exit; }')
# passwordeval "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/gmail/ { print $NF; exit; }'"
# # Mailbox
# ####################################################################################################
# account mailbox
# host smtp.mailbox.org
# port 465
# eval echo from $(gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/mailbox/ { print $(NF-2); exit; }')
# eval echo user $(gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/mailbox/ { print $(NF-2); exit; }')
# passwordeval "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/mailbox/ { print $NF; exit; }'"
# prv
account prv
# FIXME 2023-08-08: Optimise this?
eval echo from $(gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/prv/ { print $(NF-2); exit; }')
eval echo user $(gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/prv/ { print $(NF-2); exit; }')
passwordeval "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/prv/ { print $NF; exit; }'"
# Set a default account (I copied from man msmtp)
account default : pub
account default : gandi

View file

@ -1,8 +1,8 @@
#!/bin/sh
#!/bin/bash
# post-new --- Notmuch rules that run after notmuch new
# post-new --- Notmuch rules that run after notmuch-new(1)
#
# Copyright (c) 2021-2023 Protesilaos Stavrou <info@protesilaos.com>
# Copyright (C) 2021-2026 Protesilaos <info@protesilaos.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -17,19 +17,26 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
## Commentary:
### Commentary:
#
# The order of those commands matters. Maybe I will write something
# more sophisticated in the future.
# Note that the order of those commands matters.
#
# Part of my dotfiles: <https://github.com/protesilaos/dotfiles>.
### Code:
# remove "unread" from "replied"
notmuch tag -unread -new -- tag:replied
# tag all "new" messages "inbox" and "unread"
notmuch tag +inbox +unread -new -- tag:new
while IFS= read -r -d $'\0' file
do
if [[ "$file" != *R* ]]; then
notmuch tag +unread -- "path:${file}"
fi
done < <(notmuch search --output=files --format=text0 tag:new)
notmuch tag +inbox -new -- tag:new
# tag my replies as "sent"
# notmuch tag -new -unread +inbox +sent -- '(from:"*@protesilaos.com*" not to:"*@protesilaos.com*" not tag:list)'
@ -44,19 +51,4 @@ notmuch tag +coach -- date:2023-01-20..now subject:/[Tt]utor\|[Cc]oach\|[Ll]esso
# Another "mostly okay" but not 100% correct way of identifying my
# package-related messages.
notmuch tag +package -- "(to:~protesilaos/*@lists.sr.ht or to:protesilaos/*noreply.github.com or to:*incoming.gitlab.com) not tag:archived not tag:list"
# mailing lists
## emacs
notmuch tag -inbox +list +emacs -- from:emacs-devel@gnu.org or to:emacs-devel@gnu.org
notmuch tag -inbox +list +emacs -- from:emacs-orgmode@gnu.org or to:emacs-orgmode@gnu.org
notmuch tag -inbox +list +emacs -- 'to:"/*@debbugs.gnu.org*/"'
notmuch tag -inbox +list +emacs -- from:emacs-humanities@gnu.org or to:emacs-humanities@gnu.org not to:emacs-humanities-owner@gnu.org
## Others
notmuch tag -inbox +list +notmuch -- from:notmuch@notmuchmail.org or to:notmuch@notmuchmail.org
notmuch tag -inbox +list +sourcehut -- 'to:"/*~sircmpwn/sr.ht-discuss@lists.sr.ht*/"'
# include mailing lists sent to me into the "inbox"
notmuch tag +inbox -- '(to:"*@protesilaos.com*" tag:list)'
notmuch tag +package -- "(to:protesilaos/*noreply.github.com or to:*incoming.gitlab.com) not tag:archived not tag:list"

View file

@ -0,0 +1,90 @@
#!/bin/bash
# pre-new --- Notmuch rules that run before notmuch-new(1)
#
# Copyright (C) 2021-2026 Protesilaos <info@protesilaos.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
### Commentary:
#
# Move unwanted email to the appropriate local directory and tag it
# accordingly. The order of those commands matters.
#
# Part of my dotfiles: <https://github.com/protesilaos/dotfiles>.
### Code:
if [ "$HOSTNAME" = "kronos" ]
then
mail_root_path=$(notmuch config get database.path)
for tag in del spam
do
target_directory="${mail_root_path}/${tag}local/"
if [ ! -d "$target_directory" ]
then
mkdir -p "$target_directory"
fi
while IFS= read -r -d $'\0' file
do
if [ -f "$file" ]
then
mv "$file" "$target_directory$(basename -- "${file//,U=[0-9]*:/:}")"
fi
done < <(notmuch search --output=files --format=text0 tag:"$tag")
notmuch tag -inbox -"${tag}" +"${tag}"local -- tag:"$tag"
done
fi
### PROT 2025-03-01: To archive mail manually, I did the following.
### This is useful once every few years, so there is no need to
### automate it. Note that this assumes that mbsync can remove and
### expunge mail on the far/server side.
#
# Confirm that we have messages matching these search terms:
#
# notmuch count tag:"sent" from:*@protesilaos.com date:..2024-01-01
#
# Now add a tag to all of them to make it clear they are archived:
#
# notmuch tag +archivesent -sent -- tag:"sent" from:*@protesilaos.com date:..2024-01-01
#
# Confirm the count is the same for the new tag:
#
# notmuch count tag:"archivesent"
#
# Same principle for the inbox:
#
# notmuch count tag:"inbox" date:..2024-01-01
# notmuch tag +archiveinbox -inbox -- tag:"inbox" date:..2024-01-01
# notmuch count tag:"archiveinbox"
#
# Now move the messages to their respective directories, which I
# created beforehand (note that I use `echo` to make sure it works
# before doing anything---remove the `echo` to actually move the
# files).
#
# while IFS= read -r -d $'\0' file; do echo mv "$file" "$HOME"/.mail/inboxlocal/$(basename -- "${file//,U=[0-9]*:/:}") ; done < <(notmuch search --output=files --format=text0 tag:"archiveinbox" and not tag:"archivesent")
#
# while IFS= read -r -d $'\0' file; do echo mv "$file" "$HOME"/.mail/sentlocal/$(basename -- "${file//,U=[0-9]*:/:}") ; done < <(notmuch search --output=files --format=text0 tag:"archivesent")
#
# Then do an `mbsync` followed by `notmuch new` to synchronise everything.
#
# In case the aforementioned tags are confusing:
#
# notmuch tag -archiveinbox +iarchive -- tag:archiveinbox
# notmuch tag -archivesent +isent -- tag:archivesent

View file

@ -31,9 +31,9 @@ path=/home/prot/.mail
#
[user]
name=Protesilaos Stavrou
primary_email=public@protesilaos.com
other_email=info@protesilaos.com
name=Protesilaos
primary_email=prot@protesilaos.com
other_email=public@protesilaos.com;info@protesilaos.com
# Configuration for "notmuch new"
#
@ -65,7 +65,7 @@ ignore=.uidvalidity;.mbsyncstate;.mbsyncstate.new;.mbsyncstate.lock
#
[search]
exclude_tags=del;spam;
exclude_tags=
# Maildir compatibility configuration
#

View file

@ -1,11 +0,0 @@
[Desktop Entry]
Version=1.0
Name=Redshift
Exec=redshift-gtk
Icon=redshift
Terminal=false
Type=Application
Categories=Utility;
StartupNotify=true
Hidden=false
X-GNOME-Autostart-enabled=true

View file

@ -1,8 +1,8 @@
/**
* rofi -dump-theme output.
* Rofi version: 1.6.1
* User: Protesilaos Stavrou
* Copyright: Protesilaos Stavrou --- Released under the GNU GPLv3
* User: Protesilaos
* Copyright: Protesilaos --- Released under the GNU GPLv3
* Comment: Simple port of the Modus Operandi theme for Emacs (https://protesilaos.com/modus-themes)
**/

View file

@ -1,8 +1,8 @@
/**
* rofi -dump-theme output.
* Rofi version: 1.6.1
* User: Protesilaos Stavrou
* Copyright: Protesilaos Stavrou --- Released under the GNU GPLv3
* User: Protesilaos
* Copyright: Protesilaos --- Released under the GNU GPLv3
* Comment: Simple port of the Modus themes for Emacs (https://protesilaos.com/modus-themes)
**/
@ -11,8 +11,8 @@ window {
border: 5px;
border-color: @bg-alt;
padding: 10px;
width: 25%;
height: 15%;
width: 33%;
height: 8%;
/* max-height: 25%; */
}

View file

@ -1,8 +1,8 @@
/**
* rofi -dump-theme output.
* Rofi version: 1.6.1
* User: Protesilaos Stavrou
* Copyright: Protesilaos Stavrou --- Released under the GNU GPLv3
* User: Protesilaos
* Copyright: Protesilaos --- Released under the GNU GPLv3
* Comment: Simple port of the Modus Operandi theme for Emacs (https://protesilaos.com/modus-themes)
**/

View file

@ -12,7 +12,7 @@
# guidelines), refer to my Tempus themes (partially incorporated in my
# dotfiles as well): https://gitlab.com/protesilaos/tempus-themes.
#
# Copyright (c) 2019 Protesilaos Stavrou <info@protesilaos.com>
# Copyright (C) 2019-2026 Protesilaos <info@protesilaos.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by

View file

@ -2,7 +2,7 @@
# Delight (DE light) --- Toggle DE/WM and Emacs themes
#
# Copyright (c) 2020-2023 Protesilaos Stavrou <info@protesilaos.com>
# Copyright (C) 2020-2026 Protesilaos <info@protesilaos.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -29,10 +29,12 @@
#### General
gtk_theme_light="adw-gtk3"
gtk_theme_dark="adw-gtk3-dark"
icon_theme_light="Papirus"
icon_theme_dark="Papirus-Dark"
gtk_theme_light="Adwaita"
gtk_theme_dark="Adwaita-dark"
# icon_theme_light="Papirus-Light"
# icon_theme_dark="Papirus-Dark"
icon_theme_light="Adwaita"
icon_theme_dark="Adwaita"
_depcheck ()
{
@ -55,20 +57,14 @@ _emacs ()
pgrep -x emacs > /dev/null || return 1
local light_theme
local dark_theme
light_theme="modus-operandi"
dark_theme="modus-vivendi"
# Just switch to the appropriate theme for Emacs:
# https://git.sr.ht/~protesilaos/modus-themes
# https://github.com/protesilaos/modus-themes
case "$1"
in
# TODO 2023-03-01: Define a function to load appropriate Emacs
# theme.
l*) emacsclient -e "(when (and (functionp 'modus-themes--current-theme) (modus-themes--current-theme)) (modus-themes-load-theme '${light_theme}))" > /dev/null ;;
d*) emacsclient -e "(when (and (functionp 'modus-themes--current-theme) (modus-themes--current-theme)) (modus-themes-load-theme '${dark_theme}))" > /dev/null ;;
l*) emacsclient -e "(when (and (functionp 'modus-themes-get-themes) (modus-themes-get-themes)) (modus-themes-load-random-light))" > /dev/null ;;
d*) emacsclient -e "(when (and (functionp 'modus-themes-get-themes) (modus-themes-get-themes)) (modus-themes-load-random-dark))" > /dev/null ;;
esac
}
@ -82,7 +78,7 @@ _xterm ()
[ -f "$xresources" ] || { echo "No Xresources file"; exit 1; }
[ -f "$active_theme" ] || { echo "No active Xresources theme"; exit 1; }
case ${1:-$wm_theme}
case $wm_theme
in
light) _sed "s,vivendi,operandi," "$active_theme" ;;
dark) _sed "s,operandi,vivendi," "$active_theme" ;;
@ -92,7 +88,30 @@ _xterm ()
# Another one of my scripts for live recolouring running terminal
# emulators, by using escape sequences.
repaint_terminals &
repaint_terminals.sh &
}
_xfce4terminal ()
{
_depcheck xfce4-terminal
active_theme="$HOME"/.config/xfce4/terminal/terminalrc
palette_light="#000000;#a60000;#006800;#6f5500;#0031a9;#8f0075;#005e8b;#aaaaaa;#505050;#972500;#00663f;#884900;#0000b0;#531ab6;#005f5f;#ffffff;"
palette_dark="#000000;#ff5f59;#44bc44;#d0bc00;#2fafff;#f78fe7;#00d3d0;#aaaaaa;#505050;#ff6b55;#00c06f;#fec43f;#00bcff;#b6a0ff;#6ae4b9;#ffffff;"
case "$1"
in
light)
_sed "s,ColorForeground=.*,ColorForeground=#000000," "$active_theme"
_sed "s,ColorBackground=.*,ColorBackground=#ffffff," "$active_theme"
_sed "s,ColorPalette=.*,ColorPalette=$palette_light," "$active_theme"
;;
dark)
_sed "s,ColorForeground=.*,ColorForeground=#ffffff," "$active_theme"
_sed "s,ColorBackground=.*,ColorBackground=#000000," "$active_theme"
_sed "s,ColorPalette=.*,ColorPalette=$palette_dark," "$active_theme"
;;
esac
}
# TODO 2021-08-27: dunst
@ -168,8 +187,8 @@ _xsettingsd ()
if [ -f "$xsettingsd" ]
then
_sed "s,\(.*\/ThemeName\) \"[a-zA-Z-]*\",\1 \"${1}\"," "$xsettingsd"
_sed "s,\(.*\/IconThemeName\) \"[a-zA-Z-]*\",\1 \"${2}\"," "$xsettingsd"
_sed "s,\(.*\/ThemeName\) \"[0-9a-zA-Z-]*\",\1 \"${1}\"," "$xsettingsd"
_sed "s,\(.*\/IconThemeName\) \"[0-9a-zA-Z-]*\",\1 \"${2}\"," "$xsettingsd"
fi
# This is a very lightweight program that simply loads the
@ -208,6 +227,7 @@ _bspwm_or_hlwm ()
_emacs "$style" &
_xsettingsd "$style" &
_xterm &
_xfce4terminal "$style" &
# # NOTE 2021-09-25: I tried alacritty for a while, but the way it
# reads the fonts on Xorg makes the point size larger than expected.
# Also, it seems to miss some smaller sizes, so I cannot use it
@ -217,7 +237,7 @@ _bspwm_or_hlwm ()
_feh &
_rofi &
if [ -f $focus_mode_status ] && [ "$(sed 1q "$focus_mode_status")" = off ]
if [ -f "$focus_mode_status" ] && [ "$(sed 1q "$focus_mode_status")" = off ]
then
_polybar launch
else
@ -276,23 +296,16 @@ _i3 ()
#### Gsettings
# TODO 2024-04-05: Remove those functions or, at least, make them easier to remember.
_gsettings ()
{
gsettings "$1" "$2" "${@:3}"
}
# Get or set the GTK theme
_gsettings_gtk_act ()
_gsettings_gtk_act_theme_interface ()
{
_gsettings "$1" "$2" gtk-theme "${@:3}"
gsettings "$1" org.gnome.desktop.interface gtk-theme "${@:2}"
}
# Set the Color Scheme
_gsettings_color_scheme_set ()
{
_gsettings 'set' 'org.gnome.desktop.interface' 'color-scheme' "$1"
gsettings set org.gnome.desktop.interface color-scheme "$1"
}
##### Gnome
@ -300,22 +313,21 @@ _gsettings_color_scheme_set ()
_gnome_theme ()
{
_depcheck gsettings
interface="org.gnome.desktop.interface"
if [ "$(_gsettings_gtk_act 'get' $interface)" = "$gtk_theme_light" ]
if [ "$(_gsettings_gtk_act_theme_interface get)" = "'$gtk_theme_light'" ]
then
_gsettings_gtk_act 'set' $interface "$gtk_theme_dark"
_gsettings_gtk_act_theme_interface set "$gtk_theme_dark"
else
_gsettings_gtk_act 'set' $interface "$gtk_theme_light"
_gsettings_gtk_act_theme_interface set "$gtk_theme_light"
fi
if [ "$(gsettings get org.gnome.desktop.interface color-scheme)" = "'prefer-light'" ]
then
style=dark
_gsettings_color_scheme_set 'prefer-dark'
_gsettings_color_scheme_set prefer-dark
else
style=light
_gsettings_color_scheme_set 'prefer-light'
_gsettings_color_scheme_set prefer-light
fi
}
@ -340,26 +352,26 @@ _xfquery_set ()
_xfce ()
{
_depcheck gsettings
interface="org.gnome.desktop.interface"
if [ "$(_xfquery_get 'xsettings' '/Net/ThemeName')" = "$gtk_theme_light" ]
then
style=dark
# _xfquery_set 'xfwm4' '/general/theme' 'adwaita-dark'
_gsettings_gtk_act 'set' $interface "$gtk_theme_dark"
_gsettings_gtk_act_theme_interface set "$gtk_theme_dark"
_xfquery_set 'xsettings' '/Net/ThemeName' "$gtk_theme_dark"
_xfquery_set 'xsettings' '/Net/IconThemeName' "$icon_theme_dark"
_gsettings_color_scheme_set 'prefer-dark'
else
style=light
# _xfquery_set 'xfwm4' '/general/theme' 'adwaita'
_gsettings_gtk_act 'set' $interface "$gtk_theme_light"
_gsettings_gtk_act_theme_interface set "$gtk_theme_light"
_xfquery_set 'xsettings' '/Net/ThemeName' "$gtk_theme_light"
_xfquery_set 'xsettings' '/Net/IconThemeName' "$icon_theme_light"
_gsettings_color_scheme_set 'prefer-light'
fi
_emacs "$style"
_emacs "$style" &
_xfce4terminal "$style" &
}
#### Sessions
@ -368,13 +380,13 @@ case "$DESKTOP_SESSION"
in
bspwm)
wm_executable=bspwm
conf_colors=bspwm_conf_colors
conf_colors=bspwm_conf_colors.sh
_bspwm_or_hlwm
;;
herbstluftwm)
wm_executable=herbstluftwm
conf_colors=herbstluftwm_conf_colors
conf_colors=herbstluftwm_conf_colors.sh
_bspwm_or_hlwm
;;

View file

@ -2,7 +2,7 @@
# pcb --- Print the terminal's active colours.
#
# Copyright (c) 2019-2023 Protesilaos Stavrou <info@protesilaos.com>
# Copyright (C) 2019-2026 Protesilaos <info@protesilaos.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by

View file

@ -2,7 +2,7 @@
# poweroptionsmenu --- rofi or dmenu interface for session actions.
#
# Copyright (c) 2019-2023 Protesilaos Stavrou <info@protesilaos.com>
# Copyright (C) 2019-2026 Protesilaos <info@protesilaos.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -27,7 +27,7 @@ _pkill() {
}
_kill_processes() {
_pkill bspwm_external_rules
_pkill bspwm_external_rules.sh
_pkill sxhkd
_pkill polybar
_pkill feh
@ -37,9 +37,7 @@ _kill_processes() {
# In case something stops working, we can call this script
# (Ctrl+Alt+Delete on my sxhkdrc) and apply the modifications again.
_kbd_reset() {
setxkbmap -layout 'us,gr' -option '' -option 'ctrl:nocaps' \
-option 'altwin:menu_win' -option 'caps:none' \
-option 'compose:ins' -option 'grp:win_space_toggle'
setxkbmap -layout 'us,gr' -option '' -option 'grp:win_space_toggle' -option 'compose:menu'
}
_check_systemd() {

View file

@ -2,7 +2,7 @@
# tmr --- Bash script to run a simple timer
#
# Copyright (c) 2019-2023 Protesilaos Stavrou <info@protesilaos.com>
# Copyright (C) 2019-2026 Protesilaos <info@protesilaos.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by

View file

@ -18,14 +18,8 @@ _checkexec ()
### General settings
# Include my scripts in the PATH.
if [ -d "$HOME"/bin ]
then
PATH=$PATH:"$HOME"/bin
fi
if [ -d "$HOME"/.local/bin ]
then
# Set PATH so it includes user's private executables.
if [ -d "$HOME"/.local/bin ]; then
PATH=$PATH:"$HOME"/.local/bin
fi
@ -34,11 +28,6 @@ then
PATH=$PATH:"$HOME"/Builds/bin
fi
if [ -d /opt/bin ]
then
PATH=$PATH:/opt/bin
fi
if [ -d "/var/lib/flatpak/exports/bin" ]
then
PATH=$PATH:"/var/lib/flatpak/exports/bin"
@ -65,8 +54,8 @@ export SSH_AUTH_SOCK="/run/user/$(id -u)/gnupg/S.gpg-agent.ssh"
# Default editor.
if pgrep -x emacs > /dev/null
then
export VISUAL="emacsclient -c"
export EDITOR="emacsclient -t"
export EDITOR="emacsclient -a emacs -t -r"
export VISUAL="emacsclient -a emacs -t -r"
elif _checkexec gvim
then
export VISUAL="gvim"
@ -82,7 +71,7 @@ fi
# The COLORTERM is documented in (info "(emacs) General Variables").
# I found the reference to `dumb-emacs-ansi' in (info "(emacs)
# Connection Variables").
if [ "$TERM" = "dumb" ] && [ "$INSIDE_EMACS" ]
if [ "$TERM" = "dumb" ] && [ "$INSIDE_EMACS" ] || [ "$TERM" = "dumb-emacs-ansi" ] && [ "$INSIDE_EMACS" ]
then
PAGER="cat"
alias less="cat"
@ -163,6 +152,17 @@ _checkexec lesspipe && eval "$(SHELL=/bin/sh lesspipe)"
# remember the original command when necessary. There are some
# exceptions for commands I seldom execute.
#### Apt (Debian)
if _checkexec apt
then
alias aptuu="sudo apt update && sudo apt upgrade"
alias aptar="sudo apt autoremove"
alias aptss="apt search -n"
alias aptsh="apt show"
alias aptls="dpkg -L"
fi
#### Pacman and Yay (Arch Linux)
if _checkexec pacman
@ -274,7 +274,7 @@ fi
if _checkexec yt-dlp
then
alias ytaud='yt-dlp --add-metadata -ci --extract-audio --audio-format mp3 -o "%(title)s.%(ext)s"'
alias ytvid='yt-dlp --add-metadata --no-playlist --no-part --write-description --newline --prefer-free-formats -o "%(title)s.%(ext)s" '
alias ytvid='yt-dlp --add-metadata --no-playlist --no-part --write-description --newline --prefer-free-formats -o "%(title)s.%(ext)s"'
fi
# Just for fun.

View file

@ -7,7 +7,7 @@
# Configuration file for dircolors, a utility to help you set the
# LS_COLORS environment variable used by GNU ls with the --color option.
# Copyright (C) 1996-2023 Free Software Foundation, Inc.
# Copyright (C) 1996-2026 Free Software Foundation, Inc.
# Copying and distribution of this file, with or without modification,
# are permitted provided the copyright notice and this notice are preserved.
# The keywords COLOR, OPTIONS, and EIGHTBIT (honored by the

View file

@ -1,66 +0,0 @@
Socket-activated dirmngr and gpg-agent with systemd
===================================================
When used on a GNU/Linux system supervised by systemd, you can ensure
that the GnuPG daemons dirmngr and gpg-agent are launched
automatically the first time they're needed, and shut down cleanly at
session logout. This is done by enabling user services via
socket-activation.
System distributors
-------------------
The *.service and *.socket files (from this directory) should be
placed in /usr/lib/systemd/user/ alongside other user-session services
and sockets.
To enable socket-activated dirmngr for all accounts on the system,
use:
systemctl --user --global enable dirmngr.socket
To enable socket-activated gpg-agent for all accounts on the system,
use:
systemctl --user --global enable gpg-agent.socket
Additionally, you can enable socket-activated gpg-agent ssh-agent
emulation for all accounts on the system with:
systemctl --user --global enable gpg-agent-ssh.socket
You can also enable restricted ("--extra-socket"-style) gpg-agent
sockets for all accounts on the system with:
systemctl --user --global enable gpg-agent-extra.socket
Individual users
----------------
A user on a system with systemd where this has not been installed
system-wide can place these files in ~/.config/systemd/user/ to make
them available.
If a given service isn't installed system-wide, or if it's installed
system-wide but not globally enabled, individual users will still need
to enable them. For example, to enable socket-activated dirmngr for
all future sessions:
systemctl --user enable dirmngr.socket
To enable socket-activated gpg-agent with ssh support, do:
systemctl --user enable gpg-agent.socket gpg-agent-ssh.socket
These changes won't take effect until your next login after you've
fully logged out (be sure to terminate any running daemons before
logging out).
If you'd rather try a socket-activated GnuPG daemon in an
already-running session without logging out (with or without enabling
it for all future sessions), kill any existing daemon and start the
user socket directly. For example, to set up socket-activated dirmgnr
in the current session:
gpgconf --kill dirmngr
systemctl --user start dirmngr.socket

View file

@ -1 +0,0 @@
/home/prot/.config/systemd/user/dirmngr.socket

View file

@ -1 +0,0 @@
/home/prot/.config/systemd/user/gpg-agent-browser.socket

View file

@ -1 +0,0 @@
/home/prot/.config/systemd/user/gpg-agent-extra.socket

Some files were not shown because too many files have changed in this diff Show more