Lucien Cartier-Tilet
124644ff3c
`.dir-locals.el' file will automatically activate writeroom-mode for all org files and set `org-confirm-babel-evaluate' to nil.
4032 lines
154 KiB
Org Mode
4032 lines
154 KiB
Org Mode
#+title: Vanilla Emacs Configuration
|
||
#+setupfile: ~/org/config/headers
|
||
#+options: auto-id:t
|
||
#+html_head: <meta name="description" content="Phundrak’s Emacs Configuration" />
|
||
#+html_head: <meta property="og:title" content="Phundrak’s Emacs Configuration" />
|
||
#+html_head: <meta property="og:description" content="Phundrak’s Emacs Configuration Detailed" />
|
||
#+property: header-args:emacs-lisp :mkdirp yes :lexical t :exports code
|
||
#+property: header-args:emacs-lisp+ :tangle ~/.emacs.vanilla/init.el
|
||
#+property: header-args:emacs-lisp+ :mkdirp yes :noweb no-export
|
||
|
||
* Introduction
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Introduction7gzhel6184j0
|
||
:END:
|
||
#+begin_center
|
||
*STOP* Read this first!
|
||
|
||
You just landed on my vanilla Emacs configuration. However, this URL
|
||
was used until recently for my Spacemacs configuration. If you want my
|
||
complete, working Emacs configuration, I recommend you to head over
|
||
there. /*This document is still a work in progress!*/
|
||
#+end_center
|
||
|
||
After a couple of years using Spacemacs and a failed attempt at
|
||
switching to DoomEmacs, I’m finally switching back to a vanilla
|
||
configuration! Be aware though this document is still very much a work
|
||
in progress document, lots of comments on existing configuration are
|
||
missing, and lots of functionnalities are still not implemented. I’m
|
||
still in the process of porting my [[file:spacemacs.org][Spacemacs]] configuration over here.
|
||
|
||
* Basic configuration
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Basic-configurationzt3iel6184j0
|
||
:END:
|
||
** Early Init
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Basic-configuration-Early-Inityj7iel6184j0
|
||
:END:
|
||
The early init file is the file loaded before anything else in
|
||
Emacs. This is where I put some options in order to disable as quickly
|
||
as possible some built-in features of Emacs before they can be even
|
||
loaded, speeding Emacs up a bit.
|
||
#+begin_src emacs-lisp :mkdirp yes :tangle ~/.emacs.vanilla/early-init.el :exports code :results silent :lexical t
|
||
(setq package-enable-at-startup nil
|
||
inhibit-startup-message t
|
||
frame-resize-pixelwise t ; fine resize
|
||
package-native-compile t) ; native compile packages
|
||
(scroll-bar-mode -1) ; disable scrollbar
|
||
(tool-bar-mode -1) ; disable toolbar
|
||
(tooltip-mode -1) ; disable tooltips
|
||
(set-fringe-mode 10) ; give some breathing room
|
||
(menu-bar-mode -1) ; disable menubar
|
||
(blink-cursor-mode 0) ; disable blinking cursor
|
||
#+end_src
|
||
|
||
** Emacs Behavior
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Basic-configuration-Emacs-Behavior6gbiel6184j0
|
||
:END:
|
||
*** Editing Text in Emacs
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Basic-configuration-Emacs-Behavior-Editing-Text-in-Emacsy2fiel6184j0
|
||
:END:
|
||
I *never* want to keep trailing spaces in my files, which is why I’m
|
||
doing this:
|
||
#+begin_src emacs-lisp
|
||
(add-hook 'before-save-hook #'whitespace-cleanup)
|
||
#+end_src
|
||
|
||
I don’t understand why some people add two spaces behind a full stop,
|
||
I sure don’t. Let’s tell Emacs.
|
||
#+begin_src emacs-lisp
|
||
(setq sentence-end-double-space nil)
|
||
#+end_src
|
||
|
||
There is a minor mode in Emacs which allows to have a finer way of
|
||
jumping from word to word: ~global-subword-mode~. It detects if what
|
||
would usually be considered by Emacs a word can be understood as
|
||
several modes, as in camelCase words, and allows us to jump words on
|
||
this finer level.
|
||
#+begin_src emacs-lisp
|
||
(global-subword-mode 1)
|
||
#+end_src
|
||
|
||
Lastly, I want the default mode for Emacs to be Emacs Lisp.
|
||
#+begin_src emacs-lisp
|
||
(setq-default initial-major-mode 'emacs-lisp-mode)
|
||
#+end_src
|
||
|
||
**** Indentation
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Basic-configuration-Emacs-Behavior-Editing-Text-in-Emacs-Indentationauiiel6184j0
|
||
:END:
|
||
I don’t like tabs. They rarely look good, and if I need it I can
|
||
almost always tell Emacs to use them through a ~.dir-locals.el~ file or
|
||
through the config file of my code formatter. So by default, let’s
|
||
disable them:
|
||
#+begin_src emacs-lisp
|
||
(setq-default indent-tabs-mode nil)
|
||
(add-hook 'prog-mode-hook (lambda () (setq indent-tabs-mode nil)))
|
||
#+end_src
|
||
|
||
Just to go on a little tangeant here: I don’t exactly /hate/ tabs, but I
|
||
find them really annoying when your text editor knows only them. Sure,
|
||
for indentation they work great and they allow different people
|
||
getting different settings in their text editor depending on their
|
||
preferred tastes —some may prefer 2 spaces tabs, some may prefer 4
|
||
spaces tabs, some deranged people prefer 8 spaces tabs, and some
|
||
monsters prefer 3!
|
||
|
||
But the thing is, once you indented your code and then you need
|
||
alignment, tabs don’t work anymore! Or they may on *your* text editor
|
||
but not on your coworker’s! (He’s the one using 3 spaces tabs by the
|
||
way).
|
||
|
||
So, is the answer to use spaces instead of tabs, and screw peoples’
|
||
preferences in terms of tabs width? No, I say the answer is more
|
||
moderate than that, and it might frighten or anger some of you at
|
||
first: use both spaces and tabs. Now, before you lynch me on the main
|
||
avenue in front of everyone, let me tell you absolutely no one should
|
||
ever be mixing spaces and tabs for indentation, that would be
|
||
absolutely terrible and would bring the worst of both worlds. What’s
|
||
the best of both worlds then?
|
||
#+begin_center
|
||
/Tabs for indentation/
|
||
|
||
/Spaces for alignment/
|
||
#+end_center
|
||
|
||
I haven’t found a way to automate that in Emacs yet aside from
|
||
formatters’ config file, and tabs look bat in EmacsLisp anyways, so
|
||
I’ll stick with spaces by default and change it where needed.
|
||
|
||
*** Programming Modes
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Basic-configuration-Emacs-Behavior-Programming-Modesfnmiel6184j0
|
||
:END:
|
||
First off, my definition of what makes a a “programming mode” doesn’t exactly
|
||
fit mine, so on top of ~prog-mode~, let’s add a few other modes.
|
||
#+name: line-number-modes-table
|
||
| Modes |
|
||
|------------|
|
||
| prog-mode |
|
||
| latex-mode |
|
||
|
||
#+name: prog-modes-gen
|
||
#+headers: :cache yes :exports none :tangle no
|
||
#+begin_src emacs-lisp :var modes=line-number-modes-table
|
||
(mapconcat (lambda (mode) (format "%s-hook" (car mode)))
|
||
modes
|
||
" ")
|
||
#+end_src
|
||
|
||
#+RESULTS[b551840c279e88374f47f047e599b8d8686fd8bf]: prog-modes-gen
|
||
: prog-mode-hook latex-mode-hook
|
||
|
||
**** Line Number
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Basic-configuration-Emacs-Behavior-Programming-Modes-Line-Numbermcqiel6184j0
|
||
:END:
|
||
Since version 26, Emacs has a built-in capacity of displaying line
|
||
numbers on the left-side of the buffer. This is a fantastic feature
|
||
that should actually be the default for all programming modes.
|
||
|
||
#+begin_src emacs-lisp
|
||
(dolist (mode '(<<prog-modes-gen()>>))
|
||
(add-hook mode #'display-line-numbers-mode))
|
||
#+end_src
|
||
|
||
**** Folding code
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Basic-configuration-Emacs-Behavior-Programming-Modes-Folding-code16uiel6184j0
|
||
:END:
|
||
Most programming languages can usually have their code folded, be it
|
||
code between curly braces, chunks of comments or code on another level
|
||
of indentation (Python, why…?). The minor-mode that enables that is
|
||
~hs-minor-mode~, let’s enable it for all of these programming modes:
|
||
#+begin_src emacs-lisp
|
||
(dolist (mode '(<<prog-modes-gen()>>))
|
||
(add-hook mode #'hs-minor-mode))
|
||
#+end_src
|
||
|
||
*** Stay Clean, Emacs!
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Basic-configuration-Emacs-Behavior-Stay-Clean-Emacs7wxiel6184j0
|
||
:END:
|
||
As nice as Emacs is, it isn’t very polite or clean by default: open a
|
||
file, and it will create backup files in the same directory. But then,
|
||
when you open your directory with your favorite file manager and see
|
||
almost all of your files duplicated with a =~= appended to the filename,
|
||
it looks really uncomfortable! This is why I prefer to tell Emacs to
|
||
keep its backup files to itself in a directory it only will acces.
|
||
#+begin_src emacs-lisp
|
||
(setq backup-directory-alist `(("." . ,(expand-file-name ".tmp/backups/" user-emacs-directory))))
|
||
#+end_src
|
||
|
||
It also loves to litter its ~init.el~ with custom variables here and
|
||
there, but the thing is: I regenerate my ~init.el~ each time I tangle
|
||
this file! How can I keep Emacs from adding stuff that will be almost
|
||
immediately lost? Did someone say /custom file/?
|
||
#+begin_src emacs-lisp
|
||
(setq-default custom-file (expand-file-name ".custom.el" user-emacs-directory))
|
||
(when (file-exists-p custom-file) ; Don’t forget to load it, we still need it
|
||
(load custom-file))
|
||
#+end_src
|
||
|
||
If we delete a file, we want it moved to the trash, not simply deleted.
|
||
#+begin_src emacs-lisp
|
||
(setq delete-by-moving-to-trash t)
|
||
#+end_src
|
||
|
||
Finally, the scatch buffer always has some message at its beginning, I
|
||
don’t want it!
|
||
#+begin_src emacs-lisp
|
||
(setq-default initial-scratch-message nil)
|
||
#+end_src
|
||
|
||
*** Stay Polite, Emacs!
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Basic-configuration-Emacs-Behavior-Stay-Polite-Emacszp1jel6184j0
|
||
:END:
|
||
When asking for our opinion on something, Emacs loves asking us to
|
||
answer by “yes” or “no”, but *in full*! That’s very rude! Fortunately,
|
||
we can fix this.
|
||
#+begin_src emacs-lisp
|
||
(defalias 'yes-or-no-p 'y-or-n-p)
|
||
#+end_src
|
||
|
||
This will make Emacs ask us for either hitting the ~y~ key for “yes”, or
|
||
the ~n~ key for “no”. Much more polite!
|
||
|
||
It is also very impolite to keep a certain version of a file in its
|
||
buffer when said file has changed on disk. Let’s change this behavior:
|
||
#+begin_src emacs-lisp
|
||
(global-auto-revert-mode 1)
|
||
#+end_src
|
||
|
||
#+RESULTS:
|
||
: t
|
||
|
||
Much more polite! Note that if the buffer is modified and its changes
|
||
haven’t been saved, it will not automatically revert the buffer and
|
||
your unsaved changes won’t be lost. Very polite!
|
||
|
||
*** Misc
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Basic-configuration-Emacs-Behavior-Misc9j5jel6184j0
|
||
:END:
|
||
Let’s raise Emacs undo memory to 10MB, and make Emacs auto-save our
|
||
files by default.
|
||
#+begin_src emacs-lisp
|
||
(setq undo-limit 100000000
|
||
auto-save-default t)
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(setq window-combination-resize t) ; take new window space from all other windows
|
||
#+end_src
|
||
|
||
** Personal Information
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Basic-configuration-Personal-Informationi59jel6184j0
|
||
:END:
|
||
Emacs needs to know its master! For various reasons by the way, some
|
||
packages rely of these variables to know who it is talking to or
|
||
dealing with, such as ~mu4e~ which will guess who you are if you haven’t
|
||
set it up correctly.
|
||
#+begin_src emacs-lisp
|
||
(setq user-full-name "Lucien Cartier-Tilet"
|
||
user-real-login-name "Lucien Cartier-Tilet"
|
||
user-login-name "phundrak"
|
||
user-mail-address "lucien@phundrak.com")
|
||
#+end_src
|
||
|
||
** Visual Configuration
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Basic-configuration-Visual-Configurationzvcjel6184j0
|
||
:END:
|
||
The first visual setting in this section will activate the visible
|
||
bell. What it does is I get a visual feedback each time I do something
|
||
Emacs doesn’t agree with, like tring to go up a line when I’m already
|
||
at the top of the buffer.
|
||
#+begin_src emacs-lisp
|
||
(setq visible-bell t)
|
||
#+end_src
|
||
|
||
It is nicer to see a cursor cover the actual space of a character.
|
||
#+begin_src emacs-lisp
|
||
(setq x-stretch-cursor t)
|
||
#+end_src
|
||
|
||
When text is ellipsed, I want the ellipsis marker to be a single
|
||
character of three dots. Let’s make it so:
|
||
#+begin_src emacs-lisp
|
||
(setq truncate-string-ellipsis "…")
|
||
#+end_src
|
||
|
||
*** Modeline Modules
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Basic-configuration-Visual-Configuration-Modeline-Modules9kgjel6184j0
|
||
:END:
|
||
I sometimes use Emacs in fullscreen, meaning my usual taskbar will be
|
||
hidden. This is why I want the current date and time to be displayed,
|
||
in an ISO-8601 style, although not exactly ISO-8601 (this is the best
|
||
time format, fight me).
|
||
#+begin_src emacs-lisp
|
||
(setq display-time-format "%Y-%m-%d %H:%M")
|
||
(display-time-mode 1) ; display time in modeline
|
||
#+end_src
|
||
|
||
Something my taskbar doesn’t have is a battery indicator. However, I
|
||
want it enabled only if I am on a laptop or if a battery is available.
|
||
#+begin_src emacs-lisp
|
||
(let ((battery-str (battery)))
|
||
(unless (or (equal "Battery status not available" battery-str)
|
||
(string-match-p (regexp-quote "N/A") battery-str))
|
||
(display-battery-mode 1)))
|
||
#+end_src
|
||
|
||
This isn’t a modeline module per se, but we have an indicator of the
|
||
current line in Emacs. And although it is useful, I also often wish to
|
||
know which column I’m on. This can be activated like so:
|
||
#+begin_src emacs-lisp
|
||
(column-number-mode)
|
||
#+end_src
|
||
|
||
The following code is, as will several chunks of code in this config,
|
||
borrowed from [[https://tecosaur.github.io/emacs-config/#theme-modeline][TEC’s configuration]]. It hides the encoding information
|
||
of the file if the file itself is a regular UTF-8 file with ~\n~ line
|
||
ending. Be aware the ~doom-modeline-buffer-encoding~ variable is usabel
|
||
here only because I use the Doom modeline as seen below.
|
||
#+begin_src emacs-lisp
|
||
(defun modeline-contitional-buffer-encoding ()
|
||
"Hide \"LF UTF-8\" in modeline.
|
||
|
||
It is expected of files to be encoded with LF UTF-8, so only show
|
||
the encoding in the modeline if the encoding is worth notifying
|
||
the user."
|
||
(setq-local doom-modeline-buffer-encoding
|
||
(unless (and (memq (plist-get (coding-system-plist buffer-file-coding-system) :category)
|
||
'(coding-category-undecided coding-category-utf-8))
|
||
(not (memq (coding-system-eol-type buffer-file-coding-system) '(1 2))))
|
||
t)))
|
||
#+end_src
|
||
|
||
Now, let’s automate the call to this function in order to apply the
|
||
modifications to the modeline each time we open a new file.
|
||
#+begin_src emacs-lisp
|
||
(add-hook 'after-change-major-mode-hook #'modeline-contitional-buffer-encoding)
|
||
#+end_src
|
||
|
||
*** Fonts
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Basic-configuration-Visual-Configuration-Fontsxfkjel6184j0
|
||
:END:
|
||
I don’t like the default font I usually have on my machines, I really
|
||
don’t. I prefer [[https://github.com/microsoft/cascadia-code][Cascadia Code]], as it also somewhat supports the [[https://www.internationalphoneticassociation.org/][IPA]].
|
||
#+begin_src emacs-lisp
|
||
(defvar phundrak/default-font-size 90
|
||
"Default font size.")
|
||
|
||
(when (equal system-type 'gnu/linux)
|
||
(set-face-attribute 'default nil :font "Cascadia Code" :height phundrak/default-font-size))
|
||
#+end_src
|
||
|
||
*** Frame Title
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Basic-configuration-Visual-Configuration-Frame-Titlej7ojel6184j0
|
||
:END:
|
||
This is straight-up copied from [[https://tecosaur.github.io/emacs-config/config.html#window-title][TEC]]’s configuration. See their comment
|
||
on the matter.
|
||
#+begin_src emacs-lisp
|
||
(setq frame-title-format
|
||
'(""
|
||
"%b"
|
||
(:eval
|
||
(let ((project-name (projectile-project-name)))
|
||
(unless (string= "-" project-name)
|
||
(format (if (buffer-modified-p) " ◉ %s" " ● %s") project-name))))))
|
||
#+end_src
|
||
|
||
** Nice Macros From Doom-Emacs
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Basic-configuration-Nice-Macros-From-Doom-Emacsgyrjel6184j0
|
||
:END:
|
||
Doom-Emacs has some really nice macros that can come in really handy,
|
||
but since I prefer to rely on my own configuration, I’ll instead just
|
||
copy their code here. First we get the ~after!~ macro:
|
||
#+begin_src emacs-lisp
|
||
(require 'cl)
|
||
(defmacro after! (package &rest body)
|
||
"Evaluate BODY after PACKAGE have loaded.
|
||
|
||
PACKAGE is a symbol or list of them. These are package names, not modes,
|
||
functions or variables. It can be:
|
||
|
||
- An unquoted package symbol (the name of a package)
|
||
(after! helm BODY...)
|
||
- An unquoted list of package symbols (i.e. BODY is evaluated once both magit
|
||
and git-gutter have loaded)
|
||
(after! (magit git-gutter) BODY...)
|
||
- An unquoted, nested list of compound package lists, using any combination of
|
||
:or/:any and :and/:all
|
||
(after! (:or package-a package-b ...) BODY...)
|
||
(after! (:and package-a package-b ...) BODY...)
|
||
(after! (:and package-a (:or package-b package-c) ...) BODY...)
|
||
Without :or/:any/:and/:all, :and/:all are implied.
|
||
|
||
This is a wrapper around `eval-after-load' that:
|
||
|
||
1. Suppresses warnings for disabled packages at compile-time
|
||
2. Supports compound package statements (see below)
|
||
3. Prevents eager expansion pulling in autoloaded macros all at once"
|
||
(declare (indent defun) (debug t))
|
||
(if (symbolp package)
|
||
(list (if (or (not (bound-and-true-p byte-compile-current-file))
|
||
(require package nil 'noerror))
|
||
#'progn
|
||
#'with-no-warnings)
|
||
;; We intentionally avoid `with-eval-after-load' to prevent eager
|
||
;; macro expansion from pulling (or failing to pull) in autoloaded
|
||
;; macros/packages.
|
||
`(eval-after-load ',package ',(macroexp-progn body)))
|
||
(let ((p (car package)))
|
||
(cond ((not (keywordp p))
|
||
`(after! (:and ,@package) ,@body))
|
||
((memq p '(:or :any))
|
||
(macroexp-progn
|
||
(cl-loop for next in (cdr package)
|
||
collect `(after! ,next ,@body))))
|
||
((memq p '(:and :all))
|
||
(dolist (next (cdr package))
|
||
(setq body `((after! ,next ,@body))))
|
||
(car body))))))
|
||
#+end_src
|
||
|
||
* Custom Elisp
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Custom-Elispksvjel6184j0
|
||
:END:
|
||
** Dired functions
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Custom-Elisp-Dired-functionsm8zjel6184j0
|
||
:END:
|
||
*** ~phundrak/open-marked-files~
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Custom-Elisp-Dired-functions-phundrak-open-marked-filesdw2kel6184j0
|
||
:END:
|
||
This function allows the user to open all marked files from a dired
|
||
buffer as new Emacs buffers.
|
||
#+begin_src emacs-lisp
|
||
(defun phundrak/open-marked-files (&optional files)
|
||
"Open all marked FILES in dired buffer as new Emacs buffers."
|
||
(interactive)
|
||
(let* ((file-list (if files
|
||
(list files)
|
||
(if (equal major-mode "dired-mode")
|
||
(dired-get-marked-files)
|
||
(list (buffer-file-name))))))
|
||
(mapc (lambda (file-path)
|
||
(find-file file-path))
|
||
(file-list))))
|
||
#+end_src
|
||
|
||
*** ~xah/open-in-external-app~
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Custom-Elisp-Dired-functions-xah-open-in-external-appnm6kel6184j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
(defun xah/open-in-external-app (&optional file)
|
||
"Open FILE or dired marked FILE in external app.
|
||
The app is chosen from the user’s OS preference."
|
||
(interactive)
|
||
(let ((file-list (if file
|
||
(list file)
|
||
(if (equal major-mode "dired-mode")
|
||
(dired-get-marked-files)
|
||
(list (buffer-file-name)))))
|
||
(do-it-p (if (<= (length file-list) 5)
|
||
t
|
||
(y-or-n-p "Open more than 5 files? "))))
|
||
(when do-it-p
|
||
(mapc (lambda (file-path)
|
||
(let ((process-connection-type nil))
|
||
(start-process "" nil "xdg-open" file-path)))
|
||
file-list))))
|
||
#+end_src
|
||
|
||
*** ~xah/dired-sort~
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Custom-Elisp-Dired-functions-xah-dired-sort9fakel6184j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
(defun xah/dired-sort ()
|
||
"Sort dired dir listing in different ways.
|
||
Prompt for a choice."
|
||
(interactive)
|
||
(let (sort-by arg)
|
||
(setq sort-by (completing-read "Sort by:" '("name" "size" "date" "extension")))
|
||
(pcase sort-by
|
||
("name" (setq arg "-ahl --group-directories-first"))
|
||
("date" (setq arg "-ahl -t --group-directories-first"))
|
||
("size" (setq arg "-ahl -S --group-directories-first"))
|
||
("extension" (setq arg "ahlD -X --group-directories-first"))
|
||
(otherwise (error "Dired-sort: unknown option %s" otherwise)))
|
||
(dired-sort-other arg)))
|
||
#+end_src
|
||
|
||
** Switch between buffers
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Custom-Elisp-Switch-between-buffersp4ekel6184j0
|
||
:END:
|
||
Two default shortcuts I really like from Spacemacs are ~SPC b m~ and ~SPC
|
||
b s~, which bring the user directly to the ~*Messages*~ buffer and the
|
||
~*scratch*~ buffer respectively. These functions do exactly this.
|
||
#+begin_src emacs-lisp
|
||
(defun switch-to-messages-buffer ()
|
||
"Switch to Messages buffer."
|
||
(interactive)
|
||
(switch-to-buffer "*Messages*"))
|
||
|
||
(defun switch-to-scratch-buffer ()
|
||
"Switch to Messages buffer."
|
||
(interactive)
|
||
(switch-to-buffer "*scratch*"))
|
||
#+end_src
|
||
|
||
** Org Functions
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Custom-Elisp-Org-Functionsyshkel6184j0
|
||
:END:
|
||
*** Emphasize text
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Custom-Elisp-Org-Functions-Emphasize-textkilkel6184j0
|
||
:END:
|
||
| / | <c> | <c> |
|
||
| Emphasis | Character | Character code |
|
||
|----------+-----------+----------------|
|
||
| Bold | ~*~ | 42 |
|
||
| Italic | ~/~ | 47 |
|
||
| Code | ~~~ | 126 |
|
||
|
||
#+begin_src emacs-lisp
|
||
(defun org-mode-emphasize-bold ()
|
||
"Emphasize as bold the current region.
|
||
|
||
See also `org-emphasize'."
|
||
(interactive)
|
||
(org-emphasize 42))
|
||
#+end_src
|
||
|
||
*** Handle new windows
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Custom-Elisp-Org-Functions-Handle-new-windowst7pkel6184j0
|
||
:END:
|
||
The two functions below allow the user to not only create a new window
|
||
to the right or below the current window (respectively), but also to
|
||
focus the new window immediately.
|
||
#+begin_src emacs-lisp
|
||
(defun split-window-right-and-focus ()
|
||
(interactive)
|
||
(split-window-right)
|
||
(windmove-right)
|
||
(when (and (boundp 'golden-ratio-mode)
|
||
(symbol-value golden-ratio-mode))
|
||
(golden-ratio)))
|
||
|
||
(defun split-window-below-and-focus ()
|
||
(interactive)
|
||
(split-window-below)
|
||
(windmove-down)
|
||
(when (and (boundp 'golden-ratio-mode)
|
||
(symbol-value golden-ratio-mode))
|
||
(golden-ratio)))
|
||
#+end_src
|
||
|
||
*** ~phundrak/toggle-org-src-window-split~
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Custom-Elisp-Org-Functions-phundrak-toggle-org-src-window-splito2tkel6184j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
(defun phundrak/toggle-org-src-window-split ()
|
||
"This function allows the user to toggle the behavior of
|
||
`org-edit-src-code'. If the variable `org-src-window-setup' has
|
||
the value `split-window-right', then it will be changed to
|
||
`split-window-below'. Otherwise, it will be set back to
|
||
`split-window-right'"
|
||
(interactive)
|
||
(if (equal org-src-window-setup 'split-window-right)
|
||
(setq org-src-window-setup 'split-window-below)
|
||
(setq org-src-window-setup 'split-window-right))
|
||
(message "Org-src buffers will now split %s"
|
||
(if (equal org-src-window-setup 'split-window-right)
|
||
"vertically"
|
||
"horizontally")))
|
||
#+end_src
|
||
|
||
* Package Management
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Package-Managementqpwkel6184j0
|
||
:END:
|
||
** Repositories
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Package-Management-Repositoriesab0lel6184j0
|
||
:END:
|
||
By default, only GNU’s repositories are available to the package
|
||
managers of Emacs. I also want to use Melpa and org-mode’s repository,
|
||
so let’s add them! Note that the /Elpa/ repository has been renamed to
|
||
the /gnu/ repository due to the addition of another Elpa repository,
|
||
/nongnu/, which will hosts packages that do not conform to the FSF’s
|
||
copyright assignment. Both the /gnu/ and the /nonfree/ repositories are
|
||
Elpa repositories now, and they were renamed in order to avoid any
|
||
confusion between the two of them.
|
||
#+begin_src emacs-lisp
|
||
(setq package-archives '(("melpa" . "https://melpa.org/packages/")
|
||
("org" . "https://orgmode.org/elpa/")
|
||
("gnu" . "https://elpa.gnu.org/packages/")
|
||
("nongnu" . "https://elpa.nongnu.org/nongnu/")))
|
||
#+end_src
|
||
|
||
** Straight
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Package-Management-Straightry3lel6184j0
|
||
:END:
|
||
For my package management, I prefer to use ~straight~ ([[https://github.com/raxod502/straight.el][Github]]). This is
|
||
due to its capacity of integrating nicely with ~use-package~, which is
|
||
also supported by ~general~ which I use for my keybindings (see below),
|
||
but also because with it I can specify where to retrieve packages that
|
||
are not on MELPA or ELPA but on Github and other online Git
|
||
repositories too.
|
||
#+begin_src emacs-lisp
|
||
(defvar bootstrap-version)
|
||
(defvar comp-deferred-compilation-deny-list ()) ; workaround, otherwise straight shits itself
|
||
(let ((bootstrap-file
|
||
(expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
|
||
(bootstrap-version 5))
|
||
(unless (file-exists-p bootstrap-file)
|
||
(with-current-buffer
|
||
(url-retrieve-synchronously
|
||
"https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
|
||
'silent 'inhibit-cookies)
|
||
(goto-char (point-max))
|
||
(eval-print-last-sexp)))
|
||
(load bootstrap-file nil 'nomessage))
|
||
|
||
(package-initialize)
|
||
(unless package-archive-contents
|
||
(package-refresh-contents))
|
||
|
||
;; Initialize use-package on non-Linux platforms
|
||
(unless (package-installed-p 'use-package)
|
||
(package-install 'use-package))
|
||
|
||
(require 'use-package)
|
||
(setq use-package-always-ensure t)
|
||
#+end_src
|
||
|
||
* Keybinding Management
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Keybinding-Management728lel6184j0
|
||
:END:
|
||
** Which-key
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Keybinding-Management-Which-keymsblel6184j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
(use-package which-key
|
||
:straight (:build t)
|
||
:defer t
|
||
:init (which-key-mode)
|
||
:diminish which-key-mode
|
||
:config
|
||
(setq which-key-idle-delay 1))
|
||
#+end_src
|
||
|
||
** General
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Keybinding-Management-Generalycflel6184j0
|
||
:END:
|
||
General is an awesome package for managing keybindings. Not only is it
|
||
oriented towards keychords by default (which I love), but it also
|
||
provides some integration with evil so that we can declare keybindings
|
||
for certain states only! This is a perfect replacement for ~define-key~,
|
||
~evil-define-key~, and any other function for defining keychords. And it
|
||
is also possible to declare a prefix for my keybindings! By default,
|
||
my keybinds that are not bound to a specific mode will be prefixed
|
||
with ~SPC~, but when I want to get more specific in terms of mode, I'll
|
||
prefix them with a comma (I’ve taken this habit from Spacemacs).
|
||
#+begin_src emacs-lisp
|
||
(use-package general
|
||
:defer t
|
||
:straight (:build t)
|
||
:init
|
||
(general-auto-unbind-keys))
|
||
#+end_src
|
||
|
||
#+name: general-keybindings-gen
|
||
#+header: :tangle no :exports none :results value :cache yes
|
||
#+begin_src emacs-lisp :var table=mu4e-keybindings-view-tbl
|
||
(mapconcat (lambda (line)
|
||
(let ((key (car line))
|
||
(function (cadr line))
|
||
(comment (caddr line)))
|
||
(format "\"%s\" %s" key
|
||
(if (string= "" comment)
|
||
(if (string= "nil" function)
|
||
"nil"
|
||
(concat "#'" function))
|
||
(format "'(%s :wk \"%s\")"
|
||
function
|
||
(if (string= "" function)
|
||
"nil"
|
||
comment))))))
|
||
table
|
||
"\n")
|
||
#+end_src
|
||
|
||
#+RESULTS[daed5b240a2fd64608b66fc9bf38bb73f1d90db2]: general-keybindings-gen
|
||
#+begin_example
|
||
"&" #'mu4e-view-pipe
|
||
"." '(mu4e-headers-split-adjust-width/body :wk "mu4e-headers width")
|
||
"a" '(nil :wk "attachments")
|
||
"a&" #'mu4e-view-pipe-attachment
|
||
"aa" #'mu4e-view-attachment-action
|
||
"ao" #'mu4e-view-open-attachment
|
||
"aO" #'mu4e-view-open-attachment-with
|
||
"c" '(nil :wk "compose")
|
||
"cc" #'mu4e-compose-new
|
||
"ce" #'mu4e-compose-edit
|
||
"cf" #'mu4e-compose-forward
|
||
"cr" #'mu4e-compose-reply
|
||
"cR" #'mu4e-compose-resend
|
||
"g" '(nil :wk "go to")
|
||
"gu" #'mu4e-view-go-to-url
|
||
"gX" #'mu4e-view-fetch-url
|
||
"l" #'mu4e-show-log
|
||
"m" '(nil :wk "mark")
|
||
"md" #'mu4e-view-mark-for-trash
|
||
"mD" #'mu4e-view-mark-for-delete
|
||
"mm" #'mu4e-view-mark-for-move
|
||
"mr" #'mu4e-view-mark-for-refile
|
||
"mR" #'mu4e-view-mark-for-read
|
||
"mu" #'mu4e-view-mark-for-unread
|
||
"mU" #'mu4e-view-mark-for-unmark
|
||
"t" '(nil :wk "thread")
|
||
"T" '(nil :wk "toggle")
|
||
"Tc" #'mu4e-view-toggle-hide-cited
|
||
"Th" #'mu4e-view-toggle-html
|
||
"n" #'mu4e-view-headers-next
|
||
"N" #'mu4e-view-headers-next-unread
|
||
"p" #'mu4e-view-headers-prev
|
||
"P" #'mu4e-view-headers-prev-unread
|
||
#+end_example
|
||
|
||
** Evil
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Keybinding-Management-Eviljg30fl6184j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
(use-package evil
|
||
:straight (:build t)
|
||
:init
|
||
(progn
|
||
(setq evil-want-integration t
|
||
evil-want-keybinding nil
|
||
evil-want-C-u-scroll t
|
||
evil-want-C-i-jump nil)
|
||
(require 'evil-vars)
|
||
(evil-set-undo-system 'undo-tree))
|
||
:config
|
||
(evil-mode 1)
|
||
(setq evil-want-fine-undo t) ; more granular undo with evil
|
||
(evil-set-initial-state 'messages-buffer-mode 'normal)
|
||
(evil-set-initial-state 'dashboard-mode 'normal)
|
||
|
||
;; Use visual line motions even outside of visual-line-mode buffers
|
||
(evil-global-set-key 'motion "t" 'evil-next-visual-line)
|
||
(evil-global-set-key 'motion "s" 'evil-previous-visual-line)
|
||
|
||
(define-key evil-normal-state-map "c" nil)
|
||
(define-key evil-normal-state-map "C" nil)
|
||
(define-key evil-normal-state-map "t" nil)
|
||
(define-key evil-normal-state-map "T" nil)
|
||
(define-key evil-normal-state-map "s" nil)
|
||
(define-key evil-normal-state-map "S" nil)
|
||
(define-key evil-normal-state-map "r" nil)
|
||
(define-key evil-normal-state-map "R" nil)
|
||
(define-key evil-normal-state-map "h" nil)
|
||
(define-key evil-normal-state-map "H" nil)
|
||
(define-key evil-normal-state-map "j" nil)
|
||
(define-key evil-normal-state-map "J" nil)
|
||
(define-key evil-normal-state-map "k" nil)
|
||
(define-key evil-normal-state-map "K" nil)
|
||
(define-key evil-normal-state-map "l" nil)
|
||
(define-key evil-normal-state-map "L" nil)
|
||
|
||
(define-key evil-motion-state-map "h" 'evil-replace)
|
||
(define-key evil-motion-state-map "H" 'evil-replace-state)
|
||
(define-key evil-motion-state-map "j" 'evil-find-char-to)
|
||
(define-key evil-motion-state-map "J" 'evil-find-char-to-backward)
|
||
(define-key evil-motion-state-map "k" 'evil-substitute)
|
||
(define-key evil-motion-state-map "K" 'evil-smart-doc-lookup)
|
||
(define-key evil-motion-state-map "l" 'evil-change)
|
||
(define-key evil-motion-state-map "L" 'evil-change-line)
|
||
|
||
(define-key evil-motion-state-map "c" 'evil-backward-char)
|
||
(define-key evil-motion-state-map "C" 'evil-window-top)
|
||
(define-key evil-motion-state-map "t" 'evil-next-line)
|
||
(define-key evil-motion-state-map "T" 'evil-join)
|
||
(define-key evil-motion-state-map "s" 'evil-previous-line)
|
||
(define-key evil-motion-state-map "S" 'evil-lookup)
|
||
(define-key evil-motion-state-map "r" 'evil-forward-char)
|
||
(define-key evil-motion-state-map "R" 'evil-window-bottom))
|
||
|
||
|
||
(use-package evil-collection
|
||
:after evil
|
||
:straight (:build t)
|
||
:config
|
||
(evil-collection-init))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package undo-tree
|
||
:defer t
|
||
:straight (:build t)
|
||
:init
|
||
(global-undo-tree-mode))
|
||
#+end_src
|
||
|
||
** Hydra
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Keybinding-Management-Hydra0970fl6184j0
|
||
:END:
|
||
[[https://github.com/abo-abo/hydra][Hydra]] is a simple menu creator for keybindings.
|
||
#+begin_src emacs-lisp
|
||
(use-package hydra
|
||
:straight (:build t)
|
||
:defer t)
|
||
#+end_src
|
||
|
||
*** Hydras
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Keybinding-Management-Hydra-Hydrasvya0fl6184j0
|
||
:END:
|
||
The following hydra allows me to quickly zoom in and out in the
|
||
current buffer.
|
||
#+begin_src emacs-lisp
|
||
(defhydra hydra-zoom ()
|
||
"
|
||
^Zoom^ ^Other
|
||
^^^^^^^--------------------------
|
||
[_t_/_s_] zoom in/out [_q_] quit
|
||
[_0_]^^ reset zoom
|
||
"
|
||
("t" text-scale-increase "zoom in")
|
||
("s" text-scale-decrease "zoom out")
|
||
("0" text-scale-adjust "reset")
|
||
("q" nil "finished" :exit t))
|
||
#+end_src
|
||
|
||
Similarly, this one is also inspired from Spacemacs and allows the
|
||
user to interact with the width of the buffer in ~writeroom~.
|
||
#+begin_src emacs-lisp
|
||
(defhydra writeroom-buffer-width ()
|
||
"
|
||
^Width^ ^Other
|
||
^^^^^^^^-----------------------
|
||
[_t_] enlarge [_r_/_0_] adjust
|
||
[_s_] shrink [_q_]^^ quit
|
||
"
|
||
("q" nil :exit t)
|
||
("t" writeroom-increase-width "enlarge")
|
||
("s" writeroom-decrease-width "shrink")
|
||
("r" writeroom-adjust-width "adjust")
|
||
("0" writeroom-adjust-width "adjust"))
|
||
#+end_src
|
||
|
||
Another similar one is for ~mu4e-view-mode~ that allows me to shrink or
|
||
grow the ~mu4e-headers~ buffer when viewing an email.
|
||
#+begin_src emacs-lisp
|
||
(defhydra mu4e-headers-split-adjust-width ()
|
||
"
|
||
^Zoom^ ^Other
|
||
^^^^^^^---------------------------------
|
||
[_t_/_s_] shrink/enlarge view [_q_] quit
|
||
"
|
||
("q" nil :exit t)
|
||
("t" mu4e-headers-split-view-shrink "shrink")
|
||
("s" mu4e-headers-split-view-grow "enlarge"))
|
||
#+end_src
|
||
|
||
Similarly still, this one allows me to manage the size my Emacs
|
||
windows.
|
||
#+begin_src emacs-lisp
|
||
(defhydra windows-adjust-size ()
|
||
"
|
||
^Zoom^ ^Other
|
||
^^^^^^^-----------------------------------------
|
||
[_t_/_s_] shrink/enlarge vertically [_q_] quit
|
||
[_c_/_r_] shrink/enlarge horizontally
|
||
"
|
||
("q" nil :exit t)
|
||
("c" shrink-window)
|
||
("r" enlarge-window)
|
||
("t" enlarge-window-horizontally)
|
||
("s" shrink-window-horizontally))
|
||
#+end_src
|
||
|
||
* Packages Configuration
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configurationije0fl6184j0
|
||
:END:
|
||
** Applications
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Applications94i0fl6184j0
|
||
:END:
|
||
*** Docker
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Applications-Docker5ul0fl6184j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
(use-package docker
|
||
:defer t
|
||
:straight (:build t))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package dockerfile-mode
|
||
:defer t
|
||
:straight (:build t)
|
||
:init
|
||
(put 'docker-image-name 'safe-local-variable #'stringp)
|
||
:mode "Dockerfile\\'")
|
||
#+end_src
|
||
|
||
*** Elfeed
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Applications-Elfeedoip0fl6184j0
|
||
:END:
|
||
*** Email
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Applications-Email9dt0fl6184j0
|
||
:END:
|
||
**** Basic configuration
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Applications-Email-Basic-configurationf7w0fl6184j0
|
||
:END:
|
||
As seen below, I use ~org-msg~ to compose my emails, which includes by
|
||
default my signature. Therefore, there is no need for Emacs itself to
|
||
know about it since I don’t want it to include it a second time after
|
||
~org-msg~ already did.
|
||
#+begin_src emacs-lisp
|
||
(setq message-signature nil
|
||
mail-signature nil)
|
||
#+end_src
|
||
|
||
**** Mu4e
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Applications-Email-Mu4e5kx0fl6184j0
|
||
:END:
|
||
Mu4e is a very eye-pleasing email client for Emacs, built around ~mu~
|
||
and which works very well with ~mbsync~ (found in Arch’s ~isync~ package).
|
||
For me, the main advantage of mu4e is it has a modern interface for
|
||
emailing, and quite straightforward. I tried a couple of other email
|
||
clients for Emacs, and I even was for some time a Gnus user, but in
|
||
the end, mu4e really works best for me. Below you’ll find my
|
||
configuration for the ~mu4e~ package itself.
|
||
|
||
Quick sidenote: on ArchLinux, you’ll need to install either ~mu~ or
|
||
~mu-git~ from the AUR in order to use mu4e.
|
||
#+begin_src emacs-lisp
|
||
(use-package mu4e
|
||
:after all-the-icons
|
||
:straight (:build t :location site)
|
||
:commands mu4e mu4e-compose-new
|
||
:init
|
||
(progn
|
||
(setq mu4e-completing-read-function 'completing-read
|
||
mu4e-use-fancy-chars t
|
||
mu4e-view-show-images t
|
||
message-kill-buffer-on-exit t
|
||
mu4e-org-support nil)
|
||
(let ((dir "~/Downloads/mu4e"))
|
||
(when (file-directory-p dir)
|
||
(setq mu4e-attachment-dir dir))))
|
||
|
||
:config
|
||
<<mu4e-mail-service>>
|
||
<<mu4e-mail-on-machine>>
|
||
<<mu4e-no-signature>>
|
||
|
||
<<mu4e-bookmarks>>
|
||
|
||
<<mu4e-keybindings-view>>
|
||
<<mu4e-keybindings-header>>
|
||
<<mu4e-keybindings-header-no-leader>>
|
||
<<mu4e-keybindings-message>>
|
||
|
||
|
||
(when (fboundp 'imagemagick-register-types)
|
||
(imagemagick-register-types))
|
||
|
||
(add-to-list 'mu4e-view-actions '("View in browser" . mu4e-action-view-in-browser) t)
|
||
(add-to-list 'mu4e-view-actions '("PDF view" . mu4e-action-open-as-pdf) t)
|
||
|
||
(require 'gnus-dired)
|
||
(setq gnus-dired-mail-mode 'mu4e-user-agent)
|
||
|
||
(add-hook 'mu4e-compose-mode-hook (lambda () (use-hard-newlines t 'guess)))
|
||
(add-hook 'mu4e-compose-mode-hook 'mml-secure-message-sign-pgpmime)
|
||
|
||
(setq mu4e-change-filenames-when-moving t
|
||
mu4e-update-interval 60
|
||
mu4e-compose-format-flowed t
|
||
mu4e-view-show-addresses t
|
||
mu4e-sent-messages-behaviour 'sent
|
||
mu4e-hide-index-messages t
|
||
mu4e-view-show-images t ; try to show images
|
||
mu4e-view-image-max-width 600
|
||
message-send-mail-function #'smtpmail-send-it ; how to send an email
|
||
smtpmail-stream-type 'starttls
|
||
message-kill-buffer-on-exit t ; close after sending
|
||
mu4e-context-policy 'pick-first ; start with first (default) context
|
||
mu4e-compose-context-policy 'ask-if-none ; compose with current context, or ask
|
||
mu4e-completing-read-function #'ivy-completing-read ; use ivy
|
||
mu4e-confirm-quit t ; no need to ask
|
||
mu4e-header-fields '((:account . 12)
|
||
(:human-date . 12)
|
||
(:flags . 4)
|
||
(:from . 25)
|
||
(:subject)))
|
||
|
||
;; set mail user agent
|
||
(setq mail-user-agent 'mu4e-user-agent)
|
||
|
||
;; Use fancy icons
|
||
<<mu4e-fancy-marks>>
|
||
|
||
;; mu4e-headers-mode config
|
||
<<mu4e-headers-mode>>
|
||
|
||
(defun mu4e-action-open-as-pdf (msg)
|
||
"Export and open MSG as pdf."
|
||
(let* ((date (mu4e-message-field msg :date))
|
||
(infile (mu4e~write-body-to-html msg))
|
||
(outfile (format-time-string "/tmp/%Y-%m-%d-%H-%M-%S.pdf" date)))
|
||
(with-temp-buffer
|
||
(shell-command
|
||
(format "wkhtmltopdf %s %s" infile outfile) t))
|
||
(find-file outfile))))
|
||
#+end_src
|
||
|
||
***** Basic configuration
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Applications-Email-Mu4e-Basic-configurationfxy0fl6184j0
|
||
:END:
|
||
First, let’s inform Emacs how it can send emails, using which service
|
||
and how. In my case, I use my own mail server.
|
||
#+name: mu4e-mail-service
|
||
#+begin_src emacs-lisp :tangle no
|
||
(setq smtpmail-smtp-server "mail.phundrak.com"
|
||
smtpmail-smtp-service 587
|
||
smtpmail-stream-type 'starttls
|
||
message-send-mail-function 'smtpmail-send-it)
|
||
#+end_src
|
||
|
||
We also need to inform it on where my emails are stored on my machine,
|
||
and how to retrieve them.
|
||
#+name: mu4e-mail-on-machine
|
||
#+begin_src emacs-lisp :tangle no
|
||
(setq mu4e-get-mail-command "mbsync -a"
|
||
mu4e-maildir "~/Mail"
|
||
mu4e-trash-folder "/Trash"
|
||
mu4e-refile-folder "/Archive"
|
||
mu4e-sent-folder "/Sent"
|
||
mu4e-drafts-folder "/Drafts")
|
||
#+end_src
|
||
|
||
In the same vein of [[*Basic configuration][this bit of configuration]], I do not want mu4e to
|
||
insert my mail signature, ~org-msg~ already does that.
|
||
#+name: mu4e-no-signature
|
||
#+begin_src emacs-lisp :tangle no
|
||
(setq mu4e-compose-signature nil)
|
||
#+end_src
|
||
|
||
***** Actions on messages
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Applications-Email-Mu4e-Actions-on-messageskb01fl6184j0
|
||
:END:
|
||
|
||
***** Bookmarks
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Applications-Email-Mu4e-Bookmarkszo11fl6184j0
|
||
:END:
|
||
In mu4e, the main focus isn’t really mail directories such as your
|
||
inbox, your sent messages and such, but instead you manipulate
|
||
bookmarks which will show you emails depending on tags. This mean you
|
||
can create some pretty customized bookmarks that go way beyound your
|
||
simple inbox, outbox and all. Actually, four of my bookmarks have a
|
||
couple of filtering:
|
||
- anything in my inbox linked to my university
|
||
- the [[https://emacs-doctor.com/lists/listinfo][emacs-doctor mailing list]] (French Emacs mailing list)
|
||
- the conlang mailing lists
|
||
- and my inbox for any mail not caught by any of these filters
|
||
And all of them will have the requirement not to display any trashed
|
||
email. Actually, all of my bookmarks will have this requirement,
|
||
except for the bookmark dedicated to them as well as my sent emails.
|
||
I’ll add these latter requirements later.
|
||
|
||
Here are the requirements for my university bookmark. The regex
|
||
matches any email address which contains either ~up8.edu~ or
|
||
~univ-paris8~, which can be found in email addresses from the University
|
||
Paris 8 (my university).
|
||
#+name: mu4e-bookmarks-filter-uni
|
||
#+headers: :tangle no :cache yes
|
||
#+begin_src emacs-lisp
|
||
(string-join '("f:/.*up8\.edu|.*univ-paris8.*/"
|
||
"c:/.*up8\.edu|.*univ-paris8.*/"
|
||
"t:/.*up8\.edu|.*univ-paris8.*/")
|
||
" OR ")
|
||
#+end_src
|
||
|
||
#+RESULTS[6f0c2005657e701b0a992061981317febcdd6200]: mu4e-bookmarks-filter-uni
|
||
: f:/.*up8.edu|.*univ-paris8.*/ OR c:/.*up8.edu|.*univ-paris8.*/ OR t:/.*up8.edu|.*univ-paris8.*/
|
||
|
||
As for the Emacs-doctor list, I need to match both the current, modern
|
||
mailing list address but also its old address.
|
||
#+name: mu4e-bookmarks-filter-emacs-list
|
||
#+headers: :tangle no :cache yes
|
||
#+begin_src emacs-lisp
|
||
(mapconcat (lambda (address)
|
||
(mapconcat (lambda (flag)
|
||
(concat flag ":" address))
|
||
'("list" "to" "from")
|
||
" OR "))
|
||
'("ateliers-emacs.framalistes.org" "ateliers-paris.emacs-doctor.com")
|
||
" OR ")
|
||
#+end_src
|
||
|
||
#+RESULTS[cff1b5e400cca47c06057bf236d099db01411cd7]: mu4e-bookmarks-filter-emacs-list
|
||
: list:ateliers-emacs.framalistes.org OR to:ateliers-emacs.framalistes.org OR from:ateliers-emacs.framalistes.org OR list:ateliers-paris.emacs-doctor.com OR to:ateliers-paris.emacs-doctor.com OR from:ateliers-paris.emacs-doctor.com
|
||
|
||
When it comes to the conlang mailing list, let’s not match anything
|
||
from or to them. I’ll also include the auxlang mailing list –I’m not
|
||
subscribed anymore, but it’ll keep my inbox clean.
|
||
#+name: mu4e-bookmarks-filter-conlang-list
|
||
#+headers: :tangle no :cache yes
|
||
#+begin_src emacs-lisp
|
||
(mapconcat (lambda (address)
|
||
(mapconcat (lambda (flag)
|
||
(concat flag ":" address))
|
||
'("from" "to" "list")
|
||
" OR "))
|
||
'("CONLANG@LISTSERV.BROWN.EDU" "AUXLANG@LISTSERV.BROWN.EDU")
|
||
" OR ")
|
||
#+end_src
|
||
|
||
#+RESULTS[129026cfdaeb910562b800b659ad8d2d13773932]: mu4e-bookmarks-filter-conlang-list
|
||
: from:CONLANG@LISTSERV.BROWN.EDU OR to:CONLANG@LISTSERV.BROWN.EDU OR list:CONLANG@LISTSERV.BROWN.EDU OR from:AUXLANG@LISTSERV.BROWN.EDU OR to:AUXLANG@LISTSERV.BROWN.EDU OR list:AUXLANG@LISTSERV.BROWN.EDU
|
||
|
||
As I said earlier, something that will often come back in my bookmarks
|
||
is the emails must not be trashed to appear. I want also to display
|
||
junk emails, so I end up with the following rule:
|
||
#+name: mu4e-bookmarks-default-filter
|
||
#+headers: :tangle no :cache yes
|
||
#+begin_src emacs-lisp
|
||
(string-join `("NOT flag:trashed"
|
||
,(format "(%s)" (mapconcat (lambda (maildir) (concat "maildir:" maildir))
|
||
'("/Inbox" "/Junk")
|
||
" OR ")))
|
||
" AND ")
|
||
#+end_src
|
||
|
||
#+RESULTS[ccf162e159f77ccf87ff4fae220106f0a91ad256]: mu4e-bookmarks-default-filter
|
||
: NOT flag:trashed AND (maildir:/Inbox OR maildir:/Junk)
|
||
|
||
And for the last string-generating code, let’s describe my main inbox:
|
||
#+name: mu4e-bookmarks-inbox-filters
|
||
#+headers: :tangle no :cache yes
|
||
#+begin_src emacs-lisp
|
||
(string-join (cons
|
||
<<mu4e-bookmarks-default-filter>>
|
||
`(
|
||
,(format "(%s)"
|
||
<<mu4e-bookmarks-filter-conlang-list>>)
|
||
,(format "(%s)"
|
||
<<mu4e-bookmarks-filter-emacs-list>>)
|
||
,(format "(%s)"
|
||
<<mu4e-bookmarks-filter-uni>>)))
|
||
" AND NOT ")
|
||
#+end_src
|
||
|
||
#+RESULTS[2bd917f15a55a2a509f5710c6a4db5f8a8e7a596]: mu4e-bookmarks-inbox-filters
|
||
: NOT flag:trashed AND (maildir:/Inbox OR maildir:/Junk) AND NOT (from:CONLANG@LISTSERV.BROWN.EDU OR to:CONLANG@LISTSERV.BROWN.EDU OR list:CONLANG@LISTSERV.BROWN.EDU OR from:AUXLANG@LISTSERV.BROWN.EDU OR to:AUXLANG@LISTSERV.BROWN.EDU OR list:AUXLANG@LISTSERV.BROWN.EDU) AND NOT (list:ateliers-emacs.framalistes.org OR to:ateliers-emacs.framalistes.org OR from:ateliers-emacs.framalistes.org OR list:ateliers-paris.emacs-doctor.com OR to:ateliers-paris.emacs-doctor.com OR from:ateliers-paris.emacs-doctor.com) AND NOT (f:/.*up8.edu|.*univ-paris8.*/ OR c:/.*up8.edu|.*univ-paris8.*/ OR t:/.*up8.edu|.*univ-paris8.*/)
|
||
|
||
We can finally define our bookmarks! The code reads as follows:
|
||
#+name: mu4e-bookmarks
|
||
#+begin_src emacs-lisp :tangle no :cache yes
|
||
(setq mu4e-bookmarks
|
||
`((:name "Inbox"
|
||
:key ?i
|
||
:query ,(format "%s"
|
||
<<mu4e-bookmarks-inbox-filters>>))
|
||
(:name "Linguistics"
|
||
:key ?l
|
||
:query ,(format "%s AND %s"
|
||
<<mu4e-bookmarks-inbox-filters>>
|
||
<<mu4e-bookmarks-filter-conlang-list>>))
|
||
(:name "Emacs"
|
||
:key ?e
|
||
:query ,(format "%s AND %s"
|
||
<<mu4e-bookmarks-inbox-filters>>
|
||
<<mu4e-bookmarks-filter-emacs-list>>))
|
||
(:name "University"
|
||
:key ?u
|
||
:query ,(format "%s AND %s"
|
||
<<mu4e-bookmarks-inbox-filters>>
|
||
<<mu4e-bookmarks-filter-uni>>))
|
||
(:name "eshell-info-banner" :key ?E :query ,(format "%s AND %s"
|
||
<<mu4e-bookmarks-inbox-filters>>
|
||
"list:eshell-info-banner.el.Phundrak.github.com"))
|
||
(:name "Sent" :key ?s :query "maildir:/Sent")
|
||
(:name "All Unread" :key ?U :query "flag:unread AND NOT flag:trashed")
|
||
(:name "Today" :key ?t :query "date:today..now AND NOT flag:trashed")
|
||
(:name "This Week" :key ?w :query "date:7d..now AND NOT flag:trashed")
|
||
(:name "This Month" :key ?m :query "date:1m..now AND NOT flag:trashed")
|
||
(:name "This Year" :key ?y :query "date:1y..now AND NOT flag:trashed")))
|
||
#+end_src
|
||
|
||
#+RESULTS[4a18e1cfa32f399203b300f7cbd986a553a1b234]: mu4e-bookmarks
|
||
| :name | Inbox | :key | 105 | :query | NOT flag:trashed AND (maildir:/Inbox OR maildir:/Junk) AND NOT (from:CONLANG@LISTSERV.BROWN.EDU OR to:CONLANG@LISTSERV.BROWN.EDU OR list:CONLANG@LISTSERV.BROWN.EDU OR from:AUXLANG@LISTSERV.BROWN.EDU OR to:AUXLANG@LISTSERV.BROWN.EDU OR list:AUXLANG@LISTSERV.BROWN.EDU) AND NOT (list:ateliers-emacs.framalistes.org OR to:ateliers-emacs.framalistes.org OR from:ateliers-emacs.framalistes.org OR list:ateliers-paris.emacs-doctor.com OR to:ateliers-paris.emacs-doctor.com OR from:ateliers-paris.emacs-doctor.com) AND NOT (f:/.*up8.edu | .*univ-paris8.*/ OR c:/.*up8.edu | .*univ-paris8.*/ OR t:/.*up8.edu | .*univ-paris8.*/) | | | |
|
||
| :name | Linguistics | :key | 108 | :query | NOT flag:trashed AND (maildir:/Inbox OR maildir:/Junk) AND NOT (from:CONLANG@LISTSERV.BROWN.EDU OR to:CONLANG@LISTSERV.BROWN.EDU OR list:CONLANG@LISTSERV.BROWN.EDU OR from:AUXLANG@LISTSERV.BROWN.EDU OR to:AUXLANG@LISTSERV.BROWN.EDU OR list:AUXLANG@LISTSERV.BROWN.EDU) AND NOT (list:ateliers-emacs.framalistes.org OR to:ateliers-emacs.framalistes.org OR from:ateliers-emacs.framalistes.org OR list:ateliers-paris.emacs-doctor.com OR to:ateliers-paris.emacs-doctor.com OR from:ateliers-paris.emacs-doctor.com) AND NOT (f:/.*up8.edu | .*univ-paris8.*/ OR c:/.*up8.edu | .*univ-paris8.*/ OR t:/.*up8.edu | .*univ-paris8.*/) AND from:CONLANG@LISTSERV.BROWN.EDU OR to:CONLANG@LISTSERV.BROWN.EDU OR list:CONLANG@LISTSERV.BROWN.EDU OR from:AUXLANG@LISTSERV.BROWN.EDU OR to:AUXLANG@LISTSERV.BROWN.EDU OR list:AUXLANG@LISTSERV.BROWN.EDU | | | |
|
||
| :name | Emacs | :key | 101 | :query | NOT flag:trashed AND (maildir:/Inbox OR maildir:/Junk) AND NOT (from:CONLANG@LISTSERV.BROWN.EDU OR to:CONLANG@LISTSERV.BROWN.EDU OR list:CONLANG@LISTSERV.BROWN.EDU OR from:AUXLANG@LISTSERV.BROWN.EDU OR to:AUXLANG@LISTSERV.BROWN.EDU OR list:AUXLANG@LISTSERV.BROWN.EDU) AND NOT (list:ateliers-emacs.framalistes.org OR to:ateliers-emacs.framalistes.org OR from:ateliers-emacs.framalistes.org OR list:ateliers-paris.emacs-doctor.com OR to:ateliers-paris.emacs-doctor.com OR from:ateliers-paris.emacs-doctor.com) AND NOT (f:/.*up8.edu | .*univ-paris8.*/ OR c:/.*up8.edu | .*univ-paris8.*/ OR t:/.*up8.edu | .*univ-paris8.*/) AND list:ateliers-emacs.framalistes.org OR to:ateliers-emacs.framalistes.org OR from:ateliers-emacs.framalistes.org OR list:ateliers-paris.emacs-doctor.com OR to:ateliers-paris.emacs-doctor.com OR from:ateliers-paris.emacs-doctor.com | | | |
|
||
| :name | University | :key | 117 | :query | NOT flag:trashed AND (maildir:/Inbox OR maildir:/Junk) AND NOT (from:CONLANG@LISTSERV.BROWN.EDU OR to:CONLANG@LISTSERV.BROWN.EDU OR list:CONLANG@LISTSERV.BROWN.EDU OR from:AUXLANG@LISTSERV.BROWN.EDU OR to:AUXLANG@LISTSERV.BROWN.EDU OR list:AUXLANG@LISTSERV.BROWN.EDU) AND NOT (list:ateliers-emacs.framalistes.org OR to:ateliers-emacs.framalistes.org OR from:ateliers-emacs.framalistes.org OR list:ateliers-paris.emacs-doctor.com OR to:ateliers-paris.emacs-doctor.com OR from:ateliers-paris.emacs-doctor.com) AND NOT (f:/.*up8.edu | .*univ-paris8.*/ OR c:/.*up8.edu | .*univ-paris8.*/ OR t:/.*up8.edu | .*univ-paris8.*/) AND f:/.*up8.edu | .*univ-paris8.*/ OR c:/.*up8.edu | .*univ-paris8.*/ OR t:/.*up8.edu | .*univ-paris8.*/ |
|
||
| :name | eshell-info-banner | :key | 69 | :query | NOT flag:trashed AND (maildir:/Inbox OR maildir:/Junk) AND NOT (from:CONLANG@LISTSERV.BROWN.EDU OR to:CONLANG@LISTSERV.BROWN.EDU OR list:CONLANG@LISTSERV.BROWN.EDU OR from:AUXLANG@LISTSERV.BROWN.EDU OR to:AUXLANG@LISTSERV.BROWN.EDU OR list:AUXLANG@LISTSERV.BROWN.EDU) AND NOT (list:ateliers-emacs.framalistes.org OR to:ateliers-emacs.framalistes.org OR from:ateliers-emacs.framalistes.org OR list:ateliers-paris.emacs-doctor.com OR to:ateliers-paris.emacs-doctor.com OR from:ateliers-paris.emacs-doctor.com) AND NOT (f:/.*up8.edu | .*univ-paris8.*/ OR c:/.*up8.edu | .*univ-paris8.*/ OR t:/.*up8.edu | .*univ-paris8.*/) AND list:eshell-info-banner.el.Phundrak.github.com | | | |
|
||
| :name | Sent | :key | 115 | :query | maildir:/Sent | | | | | | |
|
||
| :name | All Unread | :key | 85 | :query | flag:unread AND NOT flag:trashed | | | | | | |
|
||
| :name | Today | :key | 116 | :query | date:today..now AND NOT flag:trashed | | | | | | |
|
||
| :name | This Week | :key | 119 | :query | date:7d..now AND NOT flag:trashed | | | | | | |
|
||
| :name | This Month | :key | 109 | :query | date:1m..now AND NOT flag:trashed | | | | | | |
|
||
| :name | This Year | :key | 121 | :query | date:1y..now AND NOT flag:trashed | | | | | | |
|
||
|
||
***** Getting Fancy
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Applications-Email-Mu4e-Getting-Fancyg731fl6184j0
|
||
:END:
|
||
I’m not a huge fan of mu4e’s default icons marking my emails, so I’ll
|
||
redefine them as follows. Be aware the name of these icons are from
|
||
/faicon/ in the package ~all-the-icons~.
|
||
#+name: mu4e-fancy-marks-tbl
|
||
| Mark | Flag | Icon |
|
||
|-----------+------+-------------|
|
||
| draft | D | pencil |
|
||
| flagged | F | flag |
|
||
| new | N | rss |
|
||
| passed | P | check |
|
||
| replied | R | reply |
|
||
| seen | S | eye |
|
||
| unread | u | eye-slash |
|
||
| trashed | T | trash |
|
||
| attach | a | paperclip |
|
||
| encrypted | x | lock |
|
||
| signed | s | certificate |
|
||
|
||
#+name: mu4e-fancy-marks-gen
|
||
#+header: :tangle no :exports none :results value :cache yes
|
||
#+begin_src emacs-lisp :var table=mu4e-fancy-marks-tbl
|
||
(mapconcat (lambda (line)
|
||
(let ((mark (car line))
|
||
(flag (cadr line))
|
||
(icon (caddr line)))
|
||
(format "mu4e-headers-%s-mark `(\"%s\" . ,(all-the-icons-faicon \"%s\" :height 0.8))"
|
||
mark
|
||
flag
|
||
icon)))
|
||
table
|
||
"\n")
|
||
#+end_src
|
||
|
||
#+RESULTS[c6ed5d4bec4c10339a7de52a70822af74d782e62]: mu4e-fancy-marks-gen
|
||
#+begin_example
|
||
mu4e-headers-draft-mark `("D" . ,(all-the-icons-faicon "pencil" :height 0.8))
|
||
mu4e-headers-flagged-mark `("F" . ,(all-the-icons-faicon "flag" :height 0.8))
|
||
mu4e-headers-new-mark `("N" . ,(all-the-icons-faicon "rss" :height 0.8))
|
||
mu4e-headers-passed-mark `("P" . ,(all-the-icons-faicon "check" :height 0.8))
|
||
mu4e-headers-replied-mark `("R" . ,(all-the-icons-faicon "reply" :height 0.8))
|
||
mu4e-headers-seen-mark `("S" . ,(all-the-icons-faicon "eye" :height 0.8))
|
||
mu4e-headers-unread-mark `("u" . ,(all-the-icons-faicon "eye-slash" :height 0.8))
|
||
mu4e-headers-trashed-mark `("T" . ,(all-the-icons-faicon "trash" :height 0.8))
|
||
mu4e-headers-attach-mark `("a" . ,(all-the-icons-faicon "paperclip" :height 0.8))
|
||
mu4e-headers-encrypted-mark `("x" . ,(all-the-icons-faicon "lock" :height 0.8))
|
||
mu4e-headers-signed-mark `("s" . ,(all-the-icons-faicon "certificate" :height 0.8))
|
||
#+end_example
|
||
|
||
Let’s enable them and set them:
|
||
#+name: mu4e-fancy-marks
|
||
#+begin_src emacs-lisp :tangle no
|
||
(setq mu4e-use-fancy-chars t
|
||
<<mu4e-fancy-marks-gen()>>)
|
||
#+end_src
|
||
|
||
***** Headers mode
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Applications-Email-Mu4e-Headers-modeum41fl6184j0
|
||
:END:
|
||
#+name: mu4e-headers-mode
|
||
#+begin_src emacs-lisp :tangle no
|
||
(add-hook 'mu4e-headers-mode-hook (lambda () (visual-line-mode -1)))
|
||
(add-hook 'mu4e-headers-mode-hook (lambda () (toggle-truncate-lines -1)))
|
||
#+end_src
|
||
|
||
***** Keybindings
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Applications-Email-Mu4e-Keybindingsh161fl6184j0
|
||
:END:
|
||
By default, Evil has some pretty annoying keybindings for users of the
|
||
bépo layout: ~hjkl~ becomes ~ctsr~ for us. Let’s undefine some of these:
|
||
#+name: mu4e-keybindings-undef
|
||
#+begin_src emacs-lisp :tangle no
|
||
(general-define-key
|
||
:keymaps '(mu4e-headers-mode-map mu4e-view-mode-map)
|
||
"s" nil)
|
||
(general-define-key
|
||
:states 'normal
|
||
:keymaps '(mu4e-headers-mode-map mu4e-view-mode-map)
|
||
"s" nil
|
||
"," nil)
|
||
(general-define-key
|
||
:keymaps 'mu4e-view-mode-map
|
||
"SPC" nil
|
||
"S" nil
|
||
"r" nil
|
||
"c" nil)
|
||
(general-define-key
|
||
:keymaps 'mu4e-view-mode-map
|
||
:states 'normal
|
||
"SPC" nil
|
||
"S" nil
|
||
"r" nil
|
||
"c" nil
|
||
"gu" nil)
|
||
#+end_src
|
||
|
||
#+RESULTS: mu4e-keybindings-undef
|
||
|
||
Now, let’s define some keybindings for mu4e’s view mode, that is when
|
||
we are viewing an email. All these keybindings will reside between the
|
||
major-mode specific leader key ~,~ and most of these keybindings can be
|
||
described with a simple function:
|
||
#+name: mu4e-keybindings-view-tbl
|
||
| Keybinding | Function | Description |
|
||
|------------+--------------------------------------+--------------------|
|
||
| & | mu4e-view-pipe | |
|
||
| . | mu4e-headers-split-adjust-width/body | mu4e-headers width |
|
||
| a | nil | attachments |
|
||
| a& | mu4e-view-pipe-attachment | |
|
||
| aa | mu4e-view-attachment-action | |
|
||
| ao | mu4e-view-open-attachment | |
|
||
| aO | mu4e-view-open-attachment-with | |
|
||
| c | nil | compose |
|
||
| cc | mu4e-compose-new | |
|
||
| ce | mu4e-compose-edit | |
|
||
| cf | mu4e-compose-forward | |
|
||
| cr | mu4e-compose-reply | |
|
||
| cR | mu4e-compose-resend | |
|
||
| g | nil | go to |
|
||
| gu | mu4e-view-go-to-url | |
|
||
| gX | mu4e-view-fetch-url | |
|
||
| l | mu4e-show-log | |
|
||
| m | nil | mark |
|
||
| md | mu4e-view-mark-for-trash | |
|
||
| mD | mu4e-view-mark-for-delete | |
|
||
| mm | mu4e-view-mark-for-move | |
|
||
| mr | mu4e-view-mark-for-refile | |
|
||
| mR | mu4e-view-mark-for-read | |
|
||
| mu | mu4e-view-mark-for-unread | |
|
||
| mU | mu4e-view-mark-for-unmark | |
|
||
| t | nil | thread |
|
||
| T | nil | toggle |
|
||
| Tc | mu4e-view-toggle-hide-cited | |
|
||
| Th | mu4e-view-toggle-html | |
|
||
| n | mu4e-view-headers-next | |
|
||
| N | mu4e-view-headers-next-unread | |
|
||
| p | mu4e-view-headers-prev | |
|
||
| P | mu4e-view-headers-prev-unread | |
|
||
|
||
Some functions are however lambdas. They all are written as actions on
|
||
the current thread. They all begin with ~,t~, and below you can see each
|
||
one of the possible following key can act on a thread:
|
||
#+name: mu4e-keybindings-view-lambdas-tbl
|
||
| Key | Mark thread as |
|
||
|-----+----------------|
|
||
| td | trash |
|
||
| tD | delete |
|
||
| tm | move |
|
||
| tr | refile |
|
||
| tR | read |
|
||
| tu | unread |
|
||
| tU | unmark |
|
||
|
||
#+name: mu4e-keybindings-view-lambdas-gen
|
||
#+header: :tangle no :exports none :results value
|
||
#+begin_src emacs-lisp :var table=mu4e-keybindings-view-lambdas-tbl mode="view"
|
||
(mapconcat (lambda (line)
|
||
(let ((key (car line))
|
||
(mark (cadr line)))
|
||
(format "\"%s\" '((lambda ()
|
||
(interactive)
|
||
(mu4e-%s-mark-thread '%s))
|
||
:wk \"Mark as %s\")"
|
||
key mode mark mark)))
|
||
table
|
||
"\n")
|
||
#+end_src
|
||
|
||
#+RESULTS: mu4e-keybindings-view-lambdas-gen
|
||
#+begin_example
|
||
"td" '((lambda ()
|
||
(interactive)
|
||
(mu4e-view-mark-thread 'trash))
|
||
:wk "Mark as trash")
|
||
"tD" '((lambda ()
|
||
(interactive)
|
||
(mu4e-view-mark-thread 'delete))
|
||
:wk "Mark as delete")
|
||
"tm" '((lambda ()
|
||
(interactive)
|
||
(mu4e-view-mark-thread 'move))
|
||
:wk "Mark as move")
|
||
"tr" '((lambda ()
|
||
(interactive)
|
||
(mu4e-view-mark-thread 'refile))
|
||
:wk "Mark as refile")
|
||
"tR" '((lambda ()
|
||
(interactive)
|
||
(mu4e-view-mark-thread 'read))
|
||
:wk "Mark as read")
|
||
"tu" '((lambda ()
|
||
(interactive)
|
||
(mu4e-view-mark-thread 'unread))
|
||
:wk "Mark as unread")
|
||
"tU" '((lambda ()
|
||
(interactive)
|
||
(mu4e-view-mark-thread 'unmark))
|
||
:wk "Mark as unmark")
|
||
#+end_example
|
||
|
||
#+name: mu4e-keybindings-view
|
||
#+begin_src emacs-lisp :tangle no
|
||
(general-define-key
|
||
:states 'normal
|
||
:keymaps 'mu4e-view-mode-map
|
||
:prefix ","
|
||
<<general-keybindings-gen(table=mu4e-keybindings-view-tbl)>>
|
||
<<mu4e-keybindings-view-lambdas-gen(mode="view")>>)
|
||
#+end_src
|
||
|
||
Some simple keybindings are defined for mu4e’s header mode:
|
||
#+name: mu4e-keybindings-headers-tbl
|
||
| Key | Function | Description |
|
||
|-----+----------+-------------|
|
||
| s | swiper | |
|
||
| t | nil | thread |
|
||
|
||
The keybindings from table [[mu4e-keybindings-view-lambdas-tbl]] will also
|
||
be reused for this mode.
|
||
|
||
#+name: mu4e-keybindings-header
|
||
#+begin_src emacs-lisp :tangle no
|
||
(general-define-key
|
||
:states 'normal
|
||
:keymaps 'mu4e-headers-mode-map
|
||
:prefix ","
|
||
<<general-keybindings-gen(table=mu4e-keybindings-headers-tbl)>>
|
||
<<mu4e-keybindings-view-lambdas-gen(mode="headers")>>)
|
||
#+end_src
|
||
|
||
#+RESULTS: mu4e-keybindings-header
|
||
|
||
I will also redefine without a leader key ~ctsr~ in order to be able to
|
||
move freely (remember, bépo layout for me).
|
||
#+name: mu4e-keybindings-header-no-leader
|
||
#+begin_src emacs-lisp :tangle no
|
||
(general-define-key
|
||
:keymaps 'mu4e-headers-mode-map
|
||
:states 'normal
|
||
"c" #'evil-backward-char
|
||
"t" #'evil-next-line
|
||
"s" #'evil-previous-line
|
||
"r" #'evil-forward-char)
|
||
#+end_src
|
||
|
||
#+RESULTS: mu4e-keybindings-header-no-leader
|
||
|
||
Finally, let’s declare a couple of keybindings for when we are
|
||
composing a message. This time, all my keybindings are prefixed with
|
||
the major-mode leader and call a simple function.
|
||
#+name: mu4e-keybindings-message-tbl
|
||
| Key | Function | Description |
|
||
|-----+-----------------------+-------------|
|
||
| , | message-send-and-exit | |
|
||
| c | message-send-and-exit | |
|
||
| a | message-kill-buffer | |
|
||
| k | message-kill-buffer | |
|
||
| s | message-dont-send | |
|
||
| f | mml-attach-file) | |
|
||
|
||
#+name: mu4e-keybindings-message
|
||
#+begin_src emacs-lisp :tangle no
|
||
(general-define-key
|
||
:states 'normal
|
||
:keymaps 'message-mode-map
|
||
:prefix ","
|
||
<<general-keybindings-gen(table=mu4e-keybindings-message-tbl)>>
|
||
#+end_src
|
||
|
||
#+RESULTS: mu4e-keybindings-message
|
||
|
||
**** Composing messages
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Applications-Email-Composing-messagesth71fl6184j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
(use-package org-msg
|
||
:after (org mu4e)
|
||
:straight (:build t)
|
||
:hook (mu4e-compose-pre . org-msg-mode)
|
||
:general (:keymaps 'org-msg-edit-mode-map
|
||
:prefix ","
|
||
:states 'normal
|
||
"," #'message-send-and-exit
|
||
"c" #'message-send-and-exit
|
||
"a" #'message-kill-buffer
|
||
"k" #'message-kill-buffer
|
||
"s" #'message-dont-send
|
||
"f" #'org-msg-attach)
|
||
|
||
:config
|
||
(progn
|
||
(defun my/org-msg-signature-convert (orig-fun &rest args)
|
||
"Tweak my signature when replying as plain/text only."
|
||
(let ((res (apply orig-fun args)))
|
||
(when (equal (cadr args) '(text))
|
||
(setf (alist-get 'signature res)
|
||
(replace-regexp-in-string "\n+" "\n" org-msg-signature)))
|
||
res))
|
||
(advice-add 'org-msg-composition-parameters
|
||
:around 'my/org-msg-signature-convert)
|
||
|
||
(setq org-msg-startup "inlineimages"
|
||
org-msg-default-alternatives '((new . (text html))
|
||
(reply-to-html . (text html))
|
||
(reply-to-text . (text)))
|
||
org-msg-convert-citation t
|
||
org-msg-greeting-name-limit 3
|
||
org-msg-signature (format "\n--\n#+begin_signature\n%s\n#+end_signature"
|
||
(with-temp-buffer
|
||
(insert-file-contents mail-signature-file)
|
||
(buffer-string))))))
|
||
#+end_src
|
||
|
||
**** Email alerts
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Applications-Email-Email-alertsfx81fl6184j0
|
||
:END:
|
||
There is also a package for mu4e which generates desktop notifications
|
||
when new emails are received. By default, I want to be notified by all
|
||
messages in my inbox and junk folder. Also, I’ll use Emacs’ default
|
||
notification system, and I’ll activate the modeline notification.
|
||
#+begin_src emacs-lisp
|
||
(use-package mu4e-alert
|
||
:straight (:build t)
|
||
:defer t
|
||
:init
|
||
(add-hook 'after-init-hook #'mu4e-alert-enable-notifications)
|
||
(add-hook 'after-init-hook #'mu4e-alert-enable-mode-line-display)
|
||
(mu4e-alert-set-default-style 'notifications)
|
||
:config
|
||
(setq mu4e-alert-interesting-mail-query "<<mu4e-bookmarks-default-filter()>>"))
|
||
#+end_src
|
||
|
||
*** Nov
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Applications-Nov0da1fl6184j0
|
||
:END:
|
||
Nov is a major-mode for reading EPUB files within Emacs. Since I have
|
||
it, I don’t need any other Epub reader on my computer! Plus this one
|
||
is customizable and programmable, why would I use any other EPUB
|
||
reader?
|
||
#+begin_src emacs-lisp
|
||
(use-package nov
|
||
:straight (:build t)
|
||
:defer t
|
||
:mode ("\\.epub\\'" . nov-mode)
|
||
:general
|
||
(:keymaps 'nov-mode-map
|
||
:states 'normal
|
||
"SPC" nil
|
||
"c" #'nov-previous-document
|
||
"t" #'nov-scroll-up
|
||
"C-d" #'nov-scroll-up
|
||
"s" #'nov-scroll-down
|
||
"C-u" #'nov-scroll-down
|
||
"r" #'nov-next-document
|
||
"gm" #'nov-display-metadata
|
||
"gn" #'nov-next-document
|
||
"gp" #'nov-previous-document
|
||
"gr" #'nov-render-document
|
||
"gt" #'nov-goto-toc
|
||
"gv" #'nov-view-source
|
||
"gV" #'nov-view-content-source)
|
||
:config
|
||
(setq nov-text-width 95))
|
||
#+end_src
|
||
|
||
*** PDF Tools
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Applications-PDF-Toolsvqb1fl6184j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
(use-package pdf-tools
|
||
:defer t
|
||
:magic ("%PDF" . pdf-view-mode)
|
||
:straight (:build t)
|
||
:mode (("\\.pdf\\'" . pdf-view-mode))
|
||
:config
|
||
(progn
|
||
(with-eval-after-load 'pdf-view
|
||
(setq pdf-view-midnight-colors '("#d8dee9" . "#2e3440")))
|
||
(general-define-key
|
||
:keymaps 'pdf-view-mode-map
|
||
"SPC" nil)
|
||
(general-define-key
|
||
:states 'normal
|
||
:keymaps 'pdf-view-mode-map
|
||
"SPC" nil
|
||
"y" #'pdf-view-kill-ring-save
|
||
"t" #'evil-collection-pdf-view-next-line-or-next-page
|
||
"s" #'evil-collection-pdf-view-previous-line-or-previous-page))
|
||
(general-define-key
|
||
:states 'motion
|
||
:keymaps 'pdf-view-mode-map
|
||
:prefix "SPC"
|
||
"a" '(nil :which-key "annotations")
|
||
"aD" #'pdf-annot-delete
|
||
"at" #'pdf-annot-attachment-dired
|
||
"ah" #'pdf-annot-add-highlight-markup-annotation
|
||
"al" #'pdf-annot-list-annotations
|
||
"am" #'pdf-annot-markup-annotation
|
||
"ao" #'pdf-annot-add-strikeout-markup-annotation
|
||
"as" #'pdf-annot-add-squiggly-markup-annotation
|
||
"at" #'pdf-annot-add-text-annotation
|
||
"au" #'pdf-annot-add-underline-markup-annotation
|
||
|
||
"f" '(nil :which-key "fit")
|
||
"fw" #'pdf-view-fit-width-to-window
|
||
"fh" #'pdf-view-fit-height-to-window
|
||
"fp" #'pdf-view-fit-page-to-window
|
||
|
||
"s" '(nil :which-key "slice/search")
|
||
"sb" #'pdf-view-set-slice-from-bounding-box
|
||
"sm" #'pdf-view-set-slice-using-mouse
|
||
"sr" #'pdf-view-reset-slice
|
||
"ss" #'pdf-occur
|
||
|
||
"o" 'pdf-outline
|
||
"m" 'pdf-view-midnight-minor-mode)
|
||
|
||
:hook
|
||
(pdf-tools-enabled . pdf-view-midnight-minor-mode))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package pdf-view-restore
|
||
:after pdf-tools
|
||
:hook (pdf-view-mode . pdf-view-restore-mode))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(setq pdf-view-restore-filename (expand-file-name ".tmp/pdf-view-restore"
|
||
user-emacs-directory))
|
||
#+end_src
|
||
|
||
*** Project Management
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Project-Managementi9n5fl6184j0
|
||
:END:
|
||
**** Magit
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Project-Management-Magitvso5fl6184j0
|
||
:END:
|
||
Magit is an awesome wrapper around Git for Emacs! Very often, I go
|
||
from disliking to really hating Git GUI clients because they often
|
||
obfuscate which Git commands are used to make things happen. Such a
|
||
thing doesn’t happen with Magit, it’s pretty transparent but it still
|
||
provides some awesome features and visualizations of what you are
|
||
doing and what Git is doing! In short, I absolutely love it!
|
||
#+begin_src emacs-lisp
|
||
(use-package magit
|
||
:straight (:build t)
|
||
:defer t
|
||
:custom
|
||
(magit-display-buffer-function #'magit-display-buffer-same-window-except-diff-v1)
|
||
:general
|
||
(:keymaps '(git-rebase-mode-map)
|
||
"C-t" #'evil-next-line
|
||
"C-s" #'evil-previous-line)
|
||
(:keymaps 'git-rebase-mode-map
|
||
:states 'normal
|
||
:prefix ","
|
||
"," #'with-editor-finish
|
||
"k" #'with-editor-cancel
|
||
"a" #'with-editor-cancel)
|
||
(:states 'normal
|
||
:prefix "SPC"
|
||
"g" '(nil :wk "git")
|
||
"gb" #'magit-blame
|
||
"gc" #'magit-clone
|
||
"gd" #'magit-dispatch
|
||
"gi" #'magit-init
|
||
"gs" #'magit-status
|
||
"gy" #'my/yadm
|
||
"gS" #'magit-stage-file
|
||
"gU" #'magit-unstage-file
|
||
|
||
"gf" '(nil :wk "file")
|
||
"gfd" #'magit-diff
|
||
"gfc" #'magit-file-checkout
|
||
"gfl" #'magit-file-dispatch
|
||
"gfF" #'magit-find-file))
|
||
#+end_src
|
||
|
||
[[https://github.com/alphapapa][Alphapapa]] also created an awesome package for Magit: magit-todos which
|
||
display in the Magit buffer a list of TODOs found in the current
|
||
project to remind you of what to do next.
|
||
#+begin_src emacs-lisp
|
||
(use-package magit-todos
|
||
:straight (:build t)
|
||
:after magit
|
||
:config
|
||
(setq magit-todos-ignore-case t))
|
||
#+end_src
|
||
|
||
**** Forge
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Project-Management-Forgelcq5fl6184j0
|
||
:END:
|
||
*NOTE*: Make sure to configure a GitHub token before using this
|
||
package!
|
||
- [[https://magit.vc/manual/forge/Token-Creation.html#Token-Creation][Token Creation]]
|
||
- [[https://magit.vc/manual/ghub/Getting-Started.html#Getting-Started][Getting started]]
|
||
#+begin_src emacs-lisp
|
||
(use-package forge
|
||
:after magit
|
||
:straight (:build t))
|
||
#+end_src
|
||
|
||
**** Projectile
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Project-Management-Projectilesvr5fl6184j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
(use-package projectile
|
||
:straight (:build t)
|
||
:defer t
|
||
:diminish projectile-mode
|
||
:config (projectile-mode)
|
||
:custom ((projectile-completion-system 'ivy))
|
||
:bind-keymap
|
||
("C-c p" . projectile-command-map)
|
||
:init
|
||
;; NOTE: Set this to the folder where you keep your Git repos!
|
||
(setq projectile-switch-project-action #'projectile-dired))
|
||
|
||
(use-package counsel-projectile
|
||
:straight (:build t)
|
||
:after (counsel projectile)
|
||
:config (counsel-projectile-mode))
|
||
#+end_src
|
||
|
||
*** Screenshot
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Applications-Screenshot96d1fl6184j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
(use-package screenshot
|
||
:defer t
|
||
:straight (screenshot :build t
|
||
:type git
|
||
:host github
|
||
:repo "tecosaur/screenshot"))
|
||
#+end_src
|
||
|
||
*** Shells
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Applications-Shellsxke1fl6184j0
|
||
:END:
|
||
**** Shell-pop
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Applications-Shells-Shell-popk0g1fl6184j0
|
||
:END:
|
||
Shell-pop allows the user to easily call for a new shell in a pop-up
|
||
buffer.
|
||
#+begin_src emacs-lisp
|
||
(use-package shell-pop
|
||
:defer t
|
||
:straight (:build t)
|
||
:custom
|
||
(shell-pop-default-directory "/home/phundrak")
|
||
(shell-pop-shell-type (quote ("eshell" "*eshell*" (lambda nil (eshell shell-pop-term-shell)))))
|
||
(shell-pop-window-size 30)
|
||
(shell-pop-full-span nil)
|
||
(shell-pop-window-position "bottom")
|
||
(shell-pop-autocd-to-working-dir t)
|
||
(shell-pop-restore-window-configuration t)
|
||
(shell-pop-cleanup-buffer-at-process-exit t))
|
||
#+end_src
|
||
|
||
**** VTerm
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Applications-Shells-VTermzfh1fl6184j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
(use-package vterm
|
||
:defer t
|
||
:straight t
|
||
:config
|
||
(setq vterm-shell "/usr/bin/fish"))
|
||
#+end_src
|
||
|
||
*** XWidgets Webkit Browser
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Applications-XWidgets-Webkit-Browsertui1fl6184j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
(general-define-key
|
||
:keymaps 'xwidget-webkit-mode-map
|
||
:states 'normal
|
||
"<mouse-4>" #'xwidget-webkit-scroll-down-line
|
||
"<mouse-5>" #'xwidget-webkit-scroll-up-line
|
||
|
||
"c" #'xwidget-webkit-scroll-backward
|
||
"t" #'xwidget-webkit-scroll-up-line
|
||
"s" #'xwidget-webkit-scroll-down-line
|
||
"r" #'xwidget-webkit-scroll-forward
|
||
"h" #'xwidget-webkit-goto-history
|
||
"j" nil
|
||
"k" nil
|
||
"l" nil
|
||
|
||
"H" nil
|
||
"L" nil
|
||
"C-d" #'xwidget-webkit-scroll-up
|
||
"C-u" #'xwidget-webkit-scroll-down)
|
||
|
||
(general-define-key
|
||
:keymaps 'xwidget-webkit-mode-map
|
||
:states 'normal
|
||
:prefix ","
|
||
"b" #'xwidget-webkit-back
|
||
"f" #'xwidget-webkit-forward
|
||
"r" #'xwidget-webkit-reload)
|
||
#+end_src
|
||
|
||
*** Wttr.in
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Applications-Wttr-inpak1fl6184j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
(use-package wttrin
|
||
:defer t
|
||
:straight (wttrin :build t
|
||
:local-repo "~/fromGIT/emacs-packages/emacs-wttrin"
|
||
:type git)
|
||
;; :host github
|
||
;; :repo "Phundrak/emacs-wttrin"
|
||
:config
|
||
(setq wttrin-default-cities '("Aubervilliers" "Paris" "Lyon" "Nonières" "Saint Agrève")
|
||
wttrin-use-metric t))
|
||
#+end_src
|
||
|
||
**** TODO Derive a major mode for wttrin :noexport:
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Applications-Wttr-in-Derive-a-major-mode-for-wttrinkrl1fl6184j0
|
||
:END:
|
||
To handle keybindings correctly, a major mode for wttrin could be
|
||
derived from ~fundamental-mode~ and get an associated keymap.
|
||
|
||
** Autocompletion
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Autocompletionr8n1fl6184j0
|
||
:END:
|
||
*** Code Autocompletion
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Autocompletion-Code-Autocompletion4no1fl6184j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
(use-package company
|
||
:straight (:build t)
|
||
:defer t
|
||
:hook (company-mode . evil-normalize-keymaps)
|
||
:init (global-company-mode)
|
||
:config
|
||
(setq company-minimum-prefix-length 2
|
||
company-toolsip-limit 14
|
||
company-tooltip-align-annotations t
|
||
company-require-match 'never
|
||
company-global-modes '(not erc-mode message-mode help-mode gud-mode)
|
||
company-frontends
|
||
'(company-pseudo-tooltip-frontend ; always show candidates in overlay tooltip
|
||
company-echo-metadata-frontend) ; show selected candidate docs in echo area
|
||
|
||
;; Buffer-local backends will be computed when loading a major
|
||
;; mode, so only specify a global default here.
|
||
company-backends '(company-capf)
|
||
|
||
;; These auto-complete the current selection when
|
||
;; `company-auto-complete-chars' is typed. This is too
|
||
;; magical. We already have the much more explicit RET and
|
||
;; TAB.
|
||
company-auto-complete nil
|
||
company-auto-complete-chars nil
|
||
|
||
;; Only search the current buffer for `company-dabbrev' (a
|
||
;; backend that suggests text you open buffers). This prevents
|
||
;; Company from causing lag once you have a lot of buffers
|
||
;; open.
|
||
company-dabbrev-other-buffers nil
|
||
|
||
;; Make `company-dabbrev' fully case-sensitive, to improve UX
|
||
;; with domai-specific words with particular casing.
|
||
company-dabbrev-ignore-case nil
|
||
company-dabbrev-downcase nil))
|
||
|
||
(use-package company-dict
|
||
:after company
|
||
:straight (:build t)
|
||
:config
|
||
(setq company-dict-dir (expand-file-name "dicts" user-emacs-directory)))
|
||
|
||
(use-package company-box
|
||
:straight (:build t)
|
||
:after (company all-the-icons)
|
||
:config
|
||
(setq company-box-show-single-candidate t
|
||
company-box-backends-colors nil
|
||
company-box-max-candidates 50
|
||
company-box-icons-alist 'company-box-icons-all-the-icons
|
||
company-box-icons-all-the-icons
|
||
(let ((all-the-icons-scale-factor 0.8))
|
||
`((Unknown . ,(all-the-icons-material "find_in_page" :face 'all-the-icons-purple))
|
||
(Text . ,(all-the-icons-material "text_fields" :face 'all-the-icons-green))
|
||
(Method . ,(all-the-icons-material "functions" :face 'all-the-icons-red))
|
||
(Function . ,(all-the-icons-material "functions" :face 'all-the-icons-red))
|
||
(Constructor . ,(all-the-icons-material "functions" :face 'all-the-icons-red))
|
||
(Field . ,(all-the-icons-material "functions" :face 'all-the-icons-red))
|
||
(Variable . ,(all-the-icons-material "adjust" :face 'all-the-icons-blue))
|
||
(Class . ,(all-the-icons-material "class" :face 'all-the-icons-red))
|
||
(Interface . ,(all-the-icons-material "settings_input_component" :face 'all-the-icons-red))
|
||
(Module . ,(all-the-icons-material "view_module" :face 'all-the-icons-red))
|
||
(Property . ,(all-the-icons-material "settings" :face 'all-the-icons-red))
|
||
(Unit . ,(all-the-icons-material "straighten" :face 'all-the-icons-red))
|
||
(Value . ,(all-the-icons-material "filter_1" :face 'all-the-icons-red))
|
||
(Enum . ,(all-the-icons-material "plus_one" :face 'all-the-icons-red))
|
||
(Keyword . ,(all-the-icons-material "filter_center_focus" :face 'all-the-icons-red))
|
||
(Snippet . ,(all-the-icons-material "short_text" :face 'all-the-icons-red))
|
||
(Color . ,(all-the-icons-material "color_lens" :face 'all-the-icons-red))
|
||
(File . ,(all-the-icons-material "insert_drive_file" :face 'all-the-icons-red))
|
||
(Reference . ,(all-the-icons-material "collections_bookmark" :face 'all-the-icons-red))
|
||
(Folder . ,(all-the-icons-material "folder" :face 'all-the-icons-red))
|
||
(EnumMember . ,(all-the-icons-material "people" :face 'all-the-icons-red))
|
||
(Constant . ,(all-the-icons-material "pause_circle_filled" :face 'all-the-icons-red))
|
||
(Struct . ,(all-the-icons-material "streetview" :face 'all-the-icons-red))
|
||
(Event . ,(all-the-icons-material "event" :face 'all-the-icons-red))
|
||
(Operator . ,(all-the-icons-material "control_point" :face 'all-the-icons-red))
|
||
(TypeParameter . ,(all-the-icons-material "class" :face 'all-the-icons-red))
|
||
(Template . ,(all-the-icons-material "short_text" :face 'all-the-icons-green))
|
||
(ElispFunction . ,(all-the-icons-material "functions" :face 'all-the-icons-red))
|
||
(ElispVariable . ,(all-the-icons-material "check_circle" :face 'all-the-icons-blue))
|
||
(ElispFeature . ,(all-the-icons-material "stars" :face 'all-the-icons-orange))
|
||
(ElispFace . ,(all-the-icons-material "format_paint" :face 'all-the-icons-pink))))))
|
||
#+end_src
|
||
|
||
*** Ivy
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Autocompletion-Ivy84q1fl6184j0
|
||
:END:
|
||
My main menu package is ~ivy~ which I use as much as possible –I’ve
|
||
noticed ~helm~ can be slow, very slow in comparison to ~ivy~ so I’ll use
|
||
the latter as much as possible. Actually, only ~ivy~ is installed for
|
||
now. I could have used ~ido~ too, but I find it to be a bit too
|
||
restricted in terms of features compared to ~ivy~.
|
||
#+begin_src emacs-lisp
|
||
(use-package ivy
|
||
:straight (:build t)
|
||
:defer t
|
||
:diminish
|
||
:bind (("C-s" . swiper)
|
||
:map ivy-minibuffer-map
|
||
("TAB" . ivy-alt-done)
|
||
("C-l" . ivy-alt-done)
|
||
("C-t" . ivy-next-line)
|
||
("C-s" . ivy-previous-line)
|
||
:map ivy-switch-buffer-map
|
||
("C-t" . ivy-next-line)
|
||
("C-s" . ivy-previous-line)
|
||
("C-l" . ivy-done)
|
||
("C-d" . ivy-switch-buffer-kill)
|
||
:map ivy-reverse-i-search-map
|
||
("C-t" . ivy-next-line)
|
||
("C-s" . ivy-previous-line)
|
||
("C-d" . ivy-reverse-i-search-kill))
|
||
:config
|
||
(ivy-mode 1)
|
||
(setq ivy-wrap t
|
||
ivy-height 17
|
||
ivy-sort-max-size 50000
|
||
ivy-fixed-height-minibuffer t
|
||
ivy-read-action-functions #'ivy-hydra-read-action
|
||
ivy-read-action-format-function #'ivy-read-action-format-columns
|
||
projectile-completion-system 'ivy
|
||
ivy-on-del-error-function #'ignore
|
||
ivy-use-selectable-prompt t))
|
||
#+end_src
|
||
|
||
There is also [[https://github.com/raxod502/prescient.el][~prescient.el~]] that offers some nice features when
|
||
coupled with ~ivy~, guess what was born out of it? ~ivy-prescient~, of
|
||
course!
|
||
#+begin_src emacs-lisp
|
||
(use-package ivy-prescient
|
||
:after ivy
|
||
:straight (:build t))
|
||
#+end_src
|
||
|
||
I warned you I’d use too much ~all-the-icons~, I did!
|
||
#+begin_src emacs-lisp
|
||
(use-package all-the-icons-ivy
|
||
:straight (:build t)
|
||
:after (ivy all-the-icons)
|
||
:hook (after-init . all-the-icons-ivy-setup))
|
||
#+end_src
|
||
|
||
A buffer popping at the bottom of the screen is nice and all, but have
|
||
you considered a floating buffer in the center of your frame?
|
||
#+begin_src emacs-lisp
|
||
(use-package ivy-posframe
|
||
:defer t
|
||
:hook (ivy-mode . ivy-posframe-mode)
|
||
:straight (ivy-posframe :build t
|
||
:type git
|
||
:host github
|
||
:repo "tumashu/ivy-posframe")
|
||
:config
|
||
(setq ivy-fixed-height-minibuffer nil
|
||
ivy-posframe-border-width 10
|
||
ivy-posframe-parameters
|
||
`((min-width . 90)
|
||
(min-height . ,ivy-height))))
|
||
#+end_src
|
||
|
||
Finally, let’s make ~ivy~ richer:
|
||
#+begin_src emacs-lisp
|
||
(use-package ivy-rich
|
||
:straight (:build t)
|
||
:after ivy
|
||
:init
|
||
(ivy-rich-mode 1))
|
||
#+end_src
|
||
|
||
*** Counsel
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Autocompletion-Counselorr1fl6184j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
(use-package counsel
|
||
:straight (:build t)
|
||
:defer t
|
||
:bind (("M-x" . counsel-M-x)
|
||
("C-x b" . counsel-ibuffer)
|
||
("C-x C-f" . counsel-find-file)
|
||
:map minibuffer-local-map
|
||
("C-r" . 'counsel-minibuffer-history)))
|
||
#+end_src
|
||
|
||
*** Yasnippet
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Autocompletion-Yasnippet68t1fl6184j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
(use-package yasnippet
|
||
:defer t
|
||
:straight (:build t)
|
||
:init
|
||
(yas-global-mode))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package yasnippet-snippets
|
||
:after yasnippet
|
||
:straight (:build t))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package yatemplate
|
||
:after yasnippet
|
||
:straight (:build t))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package ivy-yasnippet
|
||
:after (ivy yasnippet)
|
||
:straight (:build t))
|
||
#+end_src
|
||
|
||
** Editing
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Editinggnu1fl6184j0
|
||
:END:
|
||
*** Evil Nerd Commenter
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Editing-Evil-Nerd-Commenterd2w1fl6184j0
|
||
:END:
|
||
Emacs’ default commenting system is nice, but I don’t find it smart
|
||
enough for me.
|
||
#+begin_src emacs-lisp
|
||
(use-package evil-nerd-commenter
|
||
:after evil
|
||
:straight (:build t))
|
||
#+end_src
|
||
|
||
*** Evil Surround
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Editing-Evil-Surroundiix1fl6184j0
|
||
:END:
|
||
This package allows its user to surround regions with pairs of
|
||
characters easily.
|
||
|
||
*** Parinfer
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Editing-Parinfermxy1fl6184j0
|
||
:END:
|
||
Don’t let the name of the package fool you! ~parinfer-rust-mode~ is not
|
||
a ~parinfer~ mode for ~rust-mode~, but a mode for ~parinfer-rust~. ~parinfer~
|
||
was a project for handling parenthesis and other double markers in a
|
||
much more intuitive way when writing Lisp code. However, it is now out
|
||
of date (last commit was on January 2nd, 2019) and the repository has
|
||
since been archived. New implementations then appeared, one of them is
|
||
[[https://github.com/eraserhd/parinfer-rust][~parinfer-rust~]], obviously written in Rust, around which
|
||
~parinfer-rust-mode~ is built.
|
||
#+begin_src emacs-lisp
|
||
(use-package parinfer-rust-mode
|
||
:defer t
|
||
:diminish parinfer-rust-mode
|
||
:hook emacs-lisp-mode common-lisp-mode scheme-mode
|
||
:init
|
||
(setq parinfer-rust-auto-download t
|
||
parinfer-rust-library-directory (concat user-emacs-directory
|
||
"parinfer-rust/")))
|
||
#+end_src
|
||
|
||
*** ~string-edit~
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Editing-string-editae02fl6184j0
|
||
:END:
|
||
~string-edit~ is a cool package that allows the user to write naturally
|
||
a string and get it automatically escaped for you. No more manually
|
||
escaping your strings!
|
||
#+begin_src emacs-lisp
|
||
(use-package string-edit
|
||
:defer t
|
||
:straight (:build t))
|
||
#+end_src
|
||
|
||
*** Writeroom
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Editing-Writeroomxt12fl6184j0
|
||
:END:
|
||
On the other hand, ~writeroom~ allows the user to enter a
|
||
distraction-free mode of Emacs, and I like that! But the default width
|
||
is a bit too small for me, and I prefer not to go fullscren.
|
||
#+begin_src emacs-lisp
|
||
(use-package writeroom-mode
|
||
:defer t
|
||
:straight (:build t)
|
||
:config
|
||
(setq writeroom-width 100
|
||
writeroom-fullscreen-effect nil
|
||
writeroom-maximize-window nil))
|
||
#+end_src
|
||
|
||
** Emacs built-ins
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Emacs-built-insr832fl6184j0
|
||
:END:
|
||
*** Dired
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Emacs-built-ins-Diredao42fl6184j0
|
||
:END:
|
||
Dired is Emacs’ built-in file manager. It’s really great, and replaces
|
||
any graphical file manager for me most of the time because:
|
||
- I am not limited to /x/ tabs or panes
|
||
- All actions can be done with keybindings
|
||
- I get a consistent behavior between Dired and Emacs, since it’s the
|
||
same thing.
|
||
However, the only thing I lack still is thumbnails. In any case, I
|
||
need still to make some tweaks in order to make it usable for
|
||
me.
|
||
#+begin_src emacs-lisp
|
||
(use-package dired
|
||
:straight (:type built-in)
|
||
:defer t
|
||
:hook (dired-mode . turn-on-gnus-dired-mode)
|
||
:general
|
||
(:keymaps 'dired-mode-map
|
||
:states 'normal
|
||
"(" #'dired-hide-details-mode
|
||
"n" #'evil-next-line
|
||
"p" #'evil-previous-line)
|
||
:config
|
||
(setq dired-dwim-target t
|
||
dired-recursive-copies t
|
||
dired-recursive-deletes t
|
||
dired-listing-switches "-ahl --group-directories-first"))
|
||
#+end_src
|
||
|
||
Note that I am activating by default ~gnus-dired-mode~. This is just for
|
||
the sake of convenience, since I’m not penalized with activating this
|
||
mode when it is not needed, but I don’t have to manually activate it
|
||
each time I need it.
|
||
|
||
Dired-x stands for “dired extra” which provides a couple more features
|
||
that are for some reason not included in dired yet.
|
||
#+begin_src emacs-lisp
|
||
(use-package dired-x
|
||
:straight (:type built-in)
|
||
:after dired
|
||
:commands (dired-jump dired-jump-other-window dired-omit-mode)
|
||
:general
|
||
(:keymaps 'dired-mode-map
|
||
:states 'normal
|
||
"«" #'dired-omit-mode))
|
||
#+end_src
|
||
~dired-du~ provides the user with the option to be able to see the size
|
||
of directories as they are, rather than just the size of their
|
||
inode. However, I will not enable it by default as it can take some
|
||
time to get the actual size of a directory.
|
||
#+begin_src emacs-lisp
|
||
(use-package dired-du
|
||
:after dired
|
||
:straight (:build t)
|
||
:general
|
||
(:keymaps 'dired-mode-map
|
||
:states 'normal
|
||
"»" #'dired-du-mode))
|
||
#+end_src
|
||
|
||
This package on the other hand provides Git information to the user in
|
||
the current dired buffer in a similar way to how Github and Gitea
|
||
display the latest commit message and its age about a file in the file
|
||
tree. And by default, I want ~dired-git-info~ to hide file details.
|
||
#+begin_src emacs-lisp
|
||
(use-package dired-git-info
|
||
:after dired
|
||
:general
|
||
(:keymaps 'dired-mode-map
|
||
:states 'normal
|
||
")" #'dired-git-info-mode)
|
||
:hook (dired-after-reading . dired-git-info-auto-enable)
|
||
:config
|
||
(setq dgi-auto-hide-details-p t))
|
||
#+end_src
|
||
|
||
Diredfl makes dired colorful, and much more readable in my opinion.
|
||
#+begin_src emacs-lisp
|
||
(use-package diredfl
|
||
:straight (:build t)
|
||
:after dired
|
||
:init
|
||
(diredfl-global-mode 1))
|
||
#+end_src
|
||
|
||
And let’s add some fancy icons in dired!
|
||
#+begin_src emacs-lisp
|
||
(use-package all-the-icons-dired
|
||
:straight (:build t)
|
||
:hook (dired-mode . all-the-icons-dired-mode))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package image-dired+
|
||
:after image-dired
|
||
:init (image-diredx-adjust-mode 1))
|
||
#+end_src
|
||
|
||
*** Eshell
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Emacs-built-ins-Eshell0662fl6184j0
|
||
:END:
|
||
Eshell is a built-in shell available from Emacs which I use almost as
|
||
often as fish. Some adjustments are necessary to make it fit my taste
|
||
though.
|
||
#+begin_src emacs-lisp
|
||
(general-define-key
|
||
:keymaps 'eshell-mode-map
|
||
:states 'insert
|
||
"C-a" #'eshell-bol
|
||
"C-e" #'end-of-line)
|
||
#+end_src
|
||
|
||
**** Aliases
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Emacs-built-ins-Eshell-Aliasesom72fl6184j0
|
||
:END:
|
||
First, let’s declare our list of “dumb” aliases we’ll use in
|
||
Eshell. You can find them here.
|
||
#+begin_src emacs-lisp
|
||
(setq eshell-aliases-file (expand-file-name "eshell-aliases" user-emacs-directory))
|
||
#+end_src
|
||
|
||
A couple of other aliases will be defined through custom Elisp
|
||
functions, but first I’ll need a function for concatenating a shell
|
||
command into a single string:
|
||
#+begin_src emacs-lisp
|
||
(defun phundrak/concatenate-shell-command (&rest command)
|
||
"Concatenate an eshell `COMMAND' into a single string.
|
||
|
||
All elements of `COMMAND' will be joined in a single
|
||
space-separated string."
|
||
(string-join command " "))
|
||
#+end_src
|
||
|
||
I’ll also declare some aliases here, such as ~open~ and ~openo~ that
|
||
respectively allow me to open a file in Emacs, and same but in another
|
||
window.
|
||
#+begin_src emacs-lisp
|
||
(defalias 'open 'find-file)
|
||
(defalias 'openo 'find-file-other-window)
|
||
#+end_src
|
||
|
||
As you see, these were not declared in my dedicated aliases file but
|
||
rather were declared programmatically. This is because I like to keep
|
||
my aliases file for stuff that could work too with other shells were
|
||
the syntax a bit different, and aliases related to Elisp are kept
|
||
programmatically. I’ll also declare ~list-buffers~ an alias of ~ibuffer~
|
||
because naming it that way kind of makes more sense to me.
|
||
#+begin_src emacs-lisp
|
||
(defalias 'list-buffers 'ibuffer)
|
||
#+end_src
|
||
|
||
I still have some stupid muscle memory telling me to open ~emacs~, ~vim~
|
||
or ~nano~ in Eshell, which is stupid: I’m already inside Emacs and I
|
||
have all its power available instantly. So, let’s open each file
|
||
passed to these commands.
|
||
#+begin_src emacs-lisp
|
||
(defun eshell/emacs (&rest file)
|
||
"Open each `FILE' and kill eshell.
|
||
|
||
Old habits die hard."
|
||
(when file
|
||
(dolist (f (reverse file))
|
||
(find-file f t))))
|
||
#+end_src
|
||
|
||
Finally, I’ll declare ~mkcd~ which allows the simultaneous creation of a
|
||
directory and moving into this newly created directory. And of course,
|
||
it will also work if the directory also exists or if parent
|
||
directories don’t, similarly to the ~-p~ option passed to ~mkdir~.
|
||
#+begin_src emacs-lisp
|
||
(defun eshell/mkcd (dir)
|
||
"Create the directory `DIR' and move there.
|
||
|
||
If the directory `DIR' doesn’t exist, create it and its parents
|
||
if needed, then move there."
|
||
(mkdir dir t)
|
||
(cd dir))
|
||
#+end_src
|
||
|
||
**** Custom Functions
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Emacs-built-ins-Eshell-Custom-Functionsw492fl6184j0
|
||
:END:
|
||
When I’m in Eshell, sometimes I wish to open multiple files at once in
|
||
Emacs. For this, when I have several arguments for ~find-file~, I want
|
||
to be able to open them all at once. Let’s modify ~find-file~ like so:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defadvice find-file (around find-files activate)
|
||
"Also find all files within a list of files. This even works recursively."
|
||
(if (listp filename)
|
||
(cl-loop for f in filename do (find-file f wildcards))
|
||
ad-do-it))
|
||
#+END_SRC
|
||
|
||
I also want to be able to have multiple instances of Eshell opened at
|
||
once. For that, I declared the function ~eshell-new~ that does exactly
|
||
that.
|
||
#+begin_src emacs-lisp
|
||
(defun eshell-new ()
|
||
"Open a new instance of eshell."
|
||
(interactive)
|
||
(eshell 'N))
|
||
#+end_src
|
||
|
||
**** Environment Variables
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Emacs-built-ins-Eshell-Environment-Variablesmna2fl6184j0
|
||
:END:
|
||
Some environment variables need to be correctly set so Eshell can
|
||
correctly work. I would like to set two environment variables related
|
||
to Dart development: the ~DART_SDK~ and ~ANDROID_HOME~ variables.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setenv "DART_SDK" "/opt/dart-sdk/bin")
|
||
(setenv "ANDROID_HOME" (concat (getenv "HOME") "/Android/Sdk/"))
|
||
#+END_SRC
|
||
|
||
The ~EDITOR~ variable also needs to be set for git commands, especially the
|
||
~yadm~ commands.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setenv "EDITOR" "emacsclient -c -a emacs")
|
||
#+END_SRC
|
||
|
||
Finally, for some specific situations I need ~SHELL~ to be set to
|
||
something more standard than fish:
|
||
#+begin_src emacs-lisp
|
||
(setenv "SHELL" "/bin/sh")
|
||
#+end_src
|
||
|
||
**** Visual configuration
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Emacs-built-ins-Eshell-Visual-configuratione7c2fl6184j0
|
||
:END:
|
||
I like to have at quick glance some information about my machine when
|
||
I fire up a terminal. I haven’t found anything that does that the way
|
||
I like it, so [[https://github.com/Phundrak/eshell-info-banner.el][I’ve written a package]]!
|
||
#+begin_src emacs-lisp
|
||
(use-package eshell-info-banner
|
||
:defer t
|
||
:straight (eshell-info-banner :build t
|
||
:type git
|
||
:host github
|
||
:repo "phundrak/eshell-info-banner.el")
|
||
:hook (eshell-banner-load . eshell-info-banner-update-banner)
|
||
:config
|
||
(setq eshell-info-banner-width 80
|
||
eshell-info-banner-partition-prefixes '("/dev" "zroot" "tank")))
|
||
#+end_src
|
||
|
||
*** Tramp
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Emacs-built-ins-Tramplqd2fl6184j0
|
||
:END:
|
||
Tramp is an Emacs built-in package that allows the user to connect to
|
||
various hosts using various protocols, such as ~ssh~ and
|
||
~rsync~. However, I have some use-case for Tramp which are not
|
||
supported natively. I will describe them here.
|
||
#+begin_src emacs-lisp
|
||
(require 'tramp)
|
||
#+end_src
|
||
|
||
**** Yadm
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Emacs-built-ins-Tramp-Yadma8f2fl6184j0
|
||
:END:
|
||
[[https://yadm.io/][~yadm~]] is a git wrapper made to easily manage your dotfiles. It has
|
||
loads of features I don’t use (the main one I like but don’t use is
|
||
its [[https://yadm.io/docs/templates][Jinja-like host and OS-aware syntax]]), but unfortunately Magit
|
||
doesn’t play nice with it. Tramp to the rescue, and this page explains
|
||
how! Let’s just insert in my config this code snippet:
|
||
#+begin_src emacs-lisp
|
||
(add-to-list 'tramp-methods
|
||
'("yadm"
|
||
(tramp-login-program "yadm")
|
||
(tramp-login-args (("enter")))
|
||
(tramp-login-env (("SHELL") ("/bin/sh")))
|
||
(tramp-remote-shell "/bin/sh")
|
||
(tramp-remote-shell-args ("-c"))))
|
||
#+end_src
|
||
|
||
I’ll also create a fuction for connecting to this new Tramp protocol:
|
||
#+begin_src emacs-lisp
|
||
(defun my/yadm ()
|
||
"Manage my dotfiles through TRAMP."
|
||
(interactive)
|
||
(magit-status "/yadm::"))
|
||
#+end_src
|
||
|
||
** EXWM
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-EXWM6vx4fl6184j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
(defvar phundrak/exwm-enabled (and (seq-contains command-line-args "--with-exwm")))
|
||
|
||
(use-package xelb
|
||
:if phundrak/exwm-enabled
|
||
:straight (:build t))
|
||
|
||
(use-package exwm
|
||
:straight (:build t)
|
||
:if phundrak/exwm-enabled
|
||
:after xelb
|
||
:init
|
||
(setq mouse-autoselect-window nil
|
||
focus-follows-mouse t
|
||
exwm-workspace-warp-cursor t)
|
||
:config
|
||
(when phundrak/exwm-enabled
|
||
(setq exwm-input-global-keys
|
||
`(([?\s-r] . exwm-reset)
|
||
([?\s-w] . exwm-workspace-switch)
|
||
,@(mapcar (lambda (i)
|
||
`(,(kbd (format "s-%s" (car i))) .
|
||
(lambda ()
|
||
(interactive)
|
||
(exwm-workspace-switch-create ,(cdr i)))))
|
||
'(("\"" . 1)
|
||
("«" . 2)
|
||
("»" . 3)
|
||
("(" . 4)
|
||
(")" . 5)
|
||
("@" . 6)
|
||
("+" . 7)
|
||
("-" . 8)
|
||
("/" . 9)
|
||
("*" . 0)))))
|
||
(add-hook 'exwm-update-class-hook
|
||
(lambda ()
|
||
(exwm-workspace-rename-buffer exwm-class-name)))
|
||
(require 'exwm-config)
|
||
(exwm-config-default)
|
||
(exwm-enable)))
|
||
|
||
|
||
|
||
(use-package desktop-environment
|
||
:if phundrak/exwm-enabled
|
||
:after exwm
|
||
:straight (:build t))
|
||
#+end_src
|
||
|
||
#+RESULTS:
|
||
|
||
** Making my life easier
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Making-my-life-easier2kz4fl6184j0
|
||
:END:
|
||
*** Bufler
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Making-my-life-easier-Buflerw215fl6184j0
|
||
:END:
|
||
Bufler is a package that organizes and lists buffers in a much better
|
||
way than how they are usually sorted. You can easily and quickly find
|
||
buffers by their group, not only by their name, and THIS is great
|
||
news! Also, no ~helm~ please! And for some reasons the keybindings are
|
||
borked by default, so let’s redefine them, and let’s also rebind ~SPC~
|
||
to ~p~ since it would conflict with my main ~general~ prefix.
|
||
#+begin_src emacs-lisp
|
||
(use-package bufler
|
||
:straight (bufler :build t
|
||
:files (:defaults (:exclude "helm-bufler.el")))
|
||
:defer t
|
||
:general
|
||
(:keymaps 'bufler-list-mode-map
|
||
:states 'normal
|
||
"?" #'hydra:bufler/body
|
||
"g" #'bufler
|
||
"f" #'bufler-list-group-frame
|
||
"F" #'bufler-list-group-make-frame
|
||
"N" #'bufler-list-buffer-name-workspace
|
||
"k" #'bufler-list-buffer-kill
|
||
"p" #'bufler-list-buffer-peek
|
||
"s" #'bufler-list-buffer-save
|
||
"RET" #'bufler-list-buffer-switch))
|
||
#+end_src
|
||
|
||
*** Helpful
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Making-my-life-easier-Helpfullh25fl6184j0
|
||
:END:
|
||
As the name tells, ~helpful~ is a really helpful package which greatly
|
||
enhances a couple of built-in functions from Emacs, namely:
|
||
| Vanilla Emacs Function | Helpful Function | Comment |
|
||
|------------------------+------------------+-----------------------------------------------|
|
||
| ~describe-function~ | ~helpful-callable~ | Only interactive functions |
|
||
| ~describe-function~ | ~helpful-function~ | Only actual functions (including interactive) |
|
||
| ~describe-function~ | ~helpful-macro~ | |
|
||
| ~describe-command~ | ~helpful-command~ | |
|
||
| ~describe-key~ | ~helpful-key~ | |
|
||
| ~describe-variable~ | ~helpful-variable~ | |
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package helpful
|
||
:straight (:build t)
|
||
:after (counsel ivy)
|
||
:custom
|
||
(counsel-describe-function-function #'helpfull-callable)
|
||
(counsel-describe-variable-function #'helpfull-variable)
|
||
:bind
|
||
([remap describe-function] . counsel-describe-function)
|
||
([remap describe-command] . helpful-command)
|
||
([remap describe-variable] . counsel-describe-variable)
|
||
([remap describe-key] . helpful-key))
|
||
#+end_src
|
||
|
||
** Org-mode
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Org-modedw35fl6184j0
|
||
:END:
|
||
Org is the main reason I am using Emacs. It is an extremely powerfu
|
||
tool when you want to write anything that is not necessarily primarily
|
||
programming-related, though it absolutely can be! Org can be a
|
||
replacement for anything similar to LibreOffice Writer, LibreOffice
|
||
Calc, and LibreOffice Impress. It is a much more powerful (and older)
|
||
version of Markdown which can be exported to LaTeX and HTML at least,
|
||
rendering writing web pages and technical, scientific documents much
|
||
simpler than writing manually HTML and LaTeX code, especially when a
|
||
single document source is meant to be exported for both formats. And
|
||
since org is an Emacs package, that also means it can be greatly
|
||
extended however we like!
|
||
#+begin_src emacs-lisp
|
||
(use-package org
|
||
:straight (org :build t
|
||
:type built-in)
|
||
:defer t
|
||
:commands (orgtbl-mode)
|
||
:hook (org-mode . visual-line-mode)
|
||
:hook (org-mode . org-num-mode)
|
||
:init
|
||
(auto-fill-mode -1)
|
||
:config
|
||
<<org-hydra-babel>>
|
||
:general
|
||
(:states 'normal
|
||
:keymaps 'org-mode-map
|
||
"RET" 'org-open-at-point)
|
||
(:states 'normal
|
||
:prefix ","
|
||
:keymaps 'org-mode-map
|
||
<<general-keybindings-gen(table=org-keybinds-various)>>
|
||
<<general-keybindings-gen(table=org-keybinds-babel)>>
|
||
<<general-keybindings-gen(table=org-keybinds-dates)>>
|
||
<<general-keybindings-gen(table=org-keybinds-insert)>>
|
||
<<general-keybindings-gen(table=org-keybinds-jump)>>
|
||
<<general-keybindings-gen(table=org-keybinds-tables)>>
|
||
<<general-keybindings-gen(table=org-keybinds-toggles)>>)
|
||
|
||
(:states 'normal
|
||
:keymaps 'org-src-mode-map
|
||
:prefix ","
|
||
"'" #'org-edit-src-exit
|
||
"k" #'org-edit-src-abort))
|
||
|
||
(after! org
|
||
(setq org-hide-leading-stars nil
|
||
org-hide-macro-markers t
|
||
org-ellipsis " ⤵"
|
||
org-image-actual-width 550
|
||
org-redisplay-inline-images t
|
||
org-display-inline-images t
|
||
org-startup-with-inline-images "inlineimages"
|
||
org-pretty-entities t
|
||
org-fontify-whole-heading-line t
|
||
org-fontify-done-headline t
|
||
org-fontify-quote-and-verse-blocks t
|
||
org-startup-indented t
|
||
org-startup-align-all-tables t
|
||
org-use-property-inheritance t
|
||
org-list-allow-alphabetical t
|
||
org-M-RET-may-split-line nil
|
||
org-src-window-setup 'split-window-below
|
||
org-src-fontify-natively t
|
||
org-src-tab-acts-natively t
|
||
org-log-done 'time
|
||
org-directory "~/org"
|
||
org-default-notes-file (expand-file-name "notes.org" org-directory))
|
||
<<org-agenda-files>>
|
||
<<org-behavior-electric>>
|
||
<<org-use-sub-superscripts>>
|
||
<<org-latex-compiler>>
|
||
<<org-latex-listings>>
|
||
<<org-latex-default-packages>>
|
||
<<org-export-latex-hyperref-format>>
|
||
<<org-latex-pdf-process>>
|
||
<<org-re-reveal-root>>
|
||
<<org-html-validation>>
|
||
<<org-latex-classes>>
|
||
<<org-publish-projects>>)
|
||
#+end_src
|
||
|
||
The main feature from ~evil-org~ that I love is how easy it is to modify
|
||
some keybindings for keyboards layouts that do not have ~hjkl~, such as
|
||
the bépo layout (or Dvorak or Colemak if you are into that). But it
|
||
also adds a ton of default keybindings which are just much more
|
||
comfortable than the default ones you get with evil and org naked.
|
||
#+begin_src emacs-lisp
|
||
(use-package evil-org
|
||
:straight (:build t)
|
||
:after org
|
||
:hook (org-mode . evil-org-mode)
|
||
:config
|
||
(setq-default evil-org-movement-bindings
|
||
'((up . "s")
|
||
(down . "t")
|
||
(left . "c")
|
||
(right . "r")))
|
||
(evil-org-set-key-theme '(textobjects navigation calendar additional shift operators))
|
||
(require 'evil-org-agenda)
|
||
(evil-org-agenda-set-keys))
|
||
#+end_src
|
||
|
||
This package is a small package I’ve written that helps me when
|
||
writing conlanging documents, with features such as creating syntax
|
||
trees, converting translitterated text to its native script, etc…
|
||
#+begin_src emacs-lisp
|
||
(use-package conlanging
|
||
:straight (conlanging :build t
|
||
:type git
|
||
:host nil
|
||
:repo "https://labs.phundrak.com/phundrak/conlanging.el")
|
||
:after org
|
||
:defer t)
|
||
#+end_src
|
||
|
||
Since very recently, the ~contrib/lisp/~ directory of org moved out of
|
||
the main repository to [[https://git.sr.ht/~bzg/org-contrib][this repository]]. On the other hand,
|
||
~contrib/scripts/~ moved to [[https://code.orgmode.org/bzg/worg/src/master/code][the worg repository]], but I don’t need
|
||
it. The main reason I want ~org-contrib~ is due to ~ox-extra~ that allow
|
||
the usage of the ~:ignore:~ tag in org.
|
||
#+begin_src emacs-lisp
|
||
(use-package org-contrib
|
||
:straight (:build t)
|
||
:init
|
||
(require 'ox-extra)
|
||
(ox-extras-activate '(latex-header-blocks ignore-headlines)))
|
||
#+end_src
|
||
|
||
*** Agenda
|
||
:PROPERTIES:
|
||
:header-args:emacs-lisp: :tangle no :exports code :results silent
|
||
:CUSTOM_ID: Packages-Configuration-Org-mode-Agenda8b55fl6184j0
|
||
:END:
|
||
#+name: org-agenda-files
|
||
#+begin_src emacs-lisp
|
||
(setq-default org-agenda-files (list "~/org/agenda" "~/org/notes.org"))
|
||
#+end_src
|
||
|
||
*** Behavior
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Org-mode-Behaviorzp65fl6184j0
|
||
:END:
|
||
A useful package I like is ~toc-org~ which creates automatically a table
|
||
of contents. My main usage for this however is not just to create a
|
||
table of content of my files to quickly jump around my file (I have
|
||
~counsel-org-goto~ for that), but it is for creating table of contents
|
||
for org files that will be hosted and viewable on Github.
|
||
#+begin_src emacs-lisp
|
||
(use-package toc-org
|
||
:after org
|
||
:straight (:build t)
|
||
:init
|
||
(add-to-list 'org-tag-alist '("TOC" . ?T))
|
||
:hook (org-mode . toc-org-enable)
|
||
:hook (markdown-mode . toc-org-enable))
|
||
#+end_src
|
||
~electric-mode~ also bothers me a lot when editing org files, so let’s deactivate it:
|
||
#+name: org-behavior-electric
|
||
#+begin_src emacs-lisp
|
||
(add-hook 'org-mode-hook (lambda ()
|
||
(interactive)
|
||
(electric-indent-local-mode -1)))
|
||
#+end_src
|
||
|
||
As explained in my [[https://blog.phundrak.com/better-custom-ids-orgmode/][blog post]], org-mode is terrible with coming up with
|
||
meaningful IDs for its headings. I actually wrote a package for this!
|
||
#+begin_src emacs-lisp
|
||
(use-package org-unique-id
|
||
:straight (org-unique-id :build t
|
||
:type git
|
||
:host github
|
||
:repo "Phundrak/org-unique-id")
|
||
:defer t
|
||
:after org
|
||
:init
|
||
(add-hook 'org-mode-hook
|
||
(lambda ()
|
||
(add-hook 'before-save-hook
|
||
(lambda ()
|
||
(when (and (eq major-mode 'org-mode)
|
||
(eq buffer-read-only nil))
|
||
(org-unique-id)))))))
|
||
#+end_src
|
||
|
||
*** Babel
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Org-mode-Babel9585fl6184j0
|
||
:END:
|
||
A package I use from time to time is ~ob-latex-as-png~ which allows me
|
||
to easily convert a LaTeX snippet into a PNG, regardless of the
|
||
exporter I use afterwards. Its installation is pretty simple:
|
||
#+begin_src emacs-lisp
|
||
(use-package ob-latex-as-png
|
||
:defer t
|
||
:straight (:build t))
|
||
#+end_src
|
||
|
||
*** File export
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Org-mode-File-exportik95fl6184j0
|
||
:END:
|
||
I want to disable by default behavior of ~^~ and ~_~ for only one
|
||
character, making it compulsory to use instead ~^{}~ and ~_{}~
|
||
respectively. This is due to my frequent usage of the underscore in my
|
||
org files as a regular character and not a markup one, especially when
|
||
describing phonetics evolution. So, let’s disable it:
|
||
#+NAME: org-use-sub-superscripts
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
(setq org-use-sub-superscripts (quote {}))
|
||
#+END_SRC
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package htmlize
|
||
:defer t)
|
||
#+end_src
|
||
|
||
There’s not really any unified Markdown specification, meaning
|
||
everyone can pretty much do whatever they want with the syntax and
|
||
still call it Markdown. Great… But something I appreciate is Github
|
||
supports some extra HTML to make our files extra spicy! And lucky me,
|
||
there’s a package for exporting my org files to Github-flavored
|
||
Markdown!
|
||
#+begin_src emacs-lisp
|
||
(use-package ox-gfm
|
||
:after ox
|
||
:straight (:build t))
|
||
#+end_src
|
||
|
||
Another backend for exporting files through org I like is ~ox-epub~
|
||
which, as you can guess, exports org files to the [[https://www.w3.org/publishing/epub32/][Epub format]].
|
||
#+begin_src emacs-lisp
|
||
(use-package ox-epub
|
||
:after ox
|
||
:straight (:build t))
|
||
#+end_src
|
||
|
||
Yet another exporter I enjoy is [[https://github.com/dantecatalfamo/ox-ssh][~ox-ssh~]] with which I manage my
|
||
~$HOME/.ssh/config~ file. You won’t find my org file for managing my
|
||
servers on my repos though.
|
||
#+begin_src emacs-lisp
|
||
(use-package ox-ssh
|
||
:after ox
|
||
:straight (:build t))
|
||
#+end_src
|
||
|
||
**** LaTeX
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Org-mode-File-export-LaTeXg2b5fl6184j0
|
||
:END:
|
||
When it comes to exports, I want the LaTeX and PDF exports to be done
|
||
with XeLaTeX only. This implies the modification of the following
|
||
variable:
|
||
#+NAME: org-latex-compiler
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-latex-compiler "xelatex")
|
||
#+END_SRC
|
||
|
||
I also want to get by default ~minted~ for LaTeX listings so I can have
|
||
syntax highlights:
|
||
#+NAME: org-latex-listings
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-latex-listings 'minted)
|
||
#+END_SRC
|
||
|
||
The default packages break my LaTeX exports: for some reasons, images
|
||
are not loaded and exported in PDFs, so I needed to redifine the
|
||
default packages excluding the one that broke my exports. I also added
|
||
a default package, ~minted~ for syntax highlighting.
|
||
#+NAME: org-latex-default-packages
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-latex-default-packages-alist '(("" "graphicx" t)
|
||
("T1" "fontspec" t ("pdflatex"))
|
||
("" "longtable" nil)
|
||
("" "wrapfig" nil)
|
||
("" "rotating" nil)
|
||
("normalem" "ulem" t)
|
||
("" "amsmath" t)
|
||
("" "textcomp" t)
|
||
("" "amssymb" t)
|
||
("" "capt-of" nil)
|
||
("" "minted" nil)
|
||
("" "hyperref" nil)))
|
||
#+END_SRC
|
||
|
||
By the way, reference links in LaTeX should be written in this format:
|
||
#+NAME: org-export-latex-hyperref-format
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-export-latex-hyperref-format "\\ref{%s}")
|
||
#+END_SRC
|
||
|
||
When it comes to the export itself, the latex file needs to be
|
||
processed several times through XeLaTeX in order to get some
|
||
references right. Don’t forget to also run bibtex!
|
||
#+NAME: org-latex-pdf-process
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-latex-pdf-process
|
||
'("xelatex -8bit -shell-escape -interaction nonstopmode -output-directory %o %f"
|
||
"bibtex %b"
|
||
"xelatex -8bit -shell-escape -interaction nonstopmode -output-directory %o %f"
|
||
"xelatex -8bit -shell-escape -interaction nonstopmode -output-directory %o %f"))
|
||
#+END_SRC
|
||
|
||
Finally, org-mode is supposed to automatically clean logfiles after it
|
||
exports an org file to LaTeX. However, it misses a few, so I need to
|
||
add their extension like so:
|
||
#+begin_src emacs-lisp
|
||
(dolist (ext '("bbl" "lot"))
|
||
(add-to-list 'org-latex-logfiles-extensions ext t))
|
||
#+end_src
|
||
|
||
**** HTML
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Org-mode-File-export-HTMLxjc5fl6184j0
|
||
:END:
|
||
For Reveal.JS exports, I need to set where to find the framework by
|
||
default:
|
||
#+NAME: org-re-reveal-root
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-re-reveal-root "https://cdn.jsdelivr.net/npm/reveal.js")
|
||
#+END_SRC
|
||
|
||
On HTML exports, Org-mode tries to include a validation link for the
|
||
exported HTML. Let’s disable that since I never use it.
|
||
#+NAME: org-html-validation
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-html-validation-link nil)
|
||
#+END_SRC
|
||
|
||
*** Keybindings
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Org-mode-Keybindingsv0e5fl6184j0
|
||
:END:
|
||
Be prepared, I have a lot of keybindings for org-mode! They are all
|
||
prefixed with a comma ~,~ in normal mode.
|
||
#+name: org-keybinds-various
|
||
| Key chord | Function | Description |
|
||
|-----------+---------------------+-------------|
|
||
| RET | org-ctrl-c-ret | |
|
||
| * | org-ctrl-c-star | |
|
||
| , | org-ctrl-c-ctrl-c | |
|
||
| ' | org-edit-special | |
|
||
| - | org-ctrl-c-minus | |
|
||
| a | org-agenda | |
|
||
| c | org-capture | |
|
||
| e | org-export-dispatch | |
|
||
| l | org-store-link | |
|
||
| p | org-priority | |
|
||
| r | org-reload | |
|
||
|
||
I then have a couple of babel-related functions.
|
||
#+name: org-keybinds-babel
|
||
| Key chord | Function | Description |
|
||
|-----------+-------------------------------------+-------------|
|
||
| b | nil | babel |
|
||
| b. | org-babel-transient/body | |
|
||
| bb | org-babel-execute-buffer | |
|
||
| bc | org-babel-check-src-block | |
|
||
| bC | org-babel-tangle-clean | |
|
||
| be | org-babel-execute-maybe | |
|
||
| bf | org-babel-tangle-file | |
|
||
| bn | org-babel-next-src-block | |
|
||
| bo | org-babel-open-src-block-result | |
|
||
| bp | org-babel-previous-src-block | |
|
||
| br | org-babel-remove-result-one-or-many | |
|
||
| bR | org-babel-goto-named-result | |
|
||
| bt | org-babel-tangle | |
|
||
| bi | org-babel-view-src-block-info | |
|
||
|
||
The ~org-babel-transient~ hydra allows me to quickly navigate between
|
||
code blocks and interact with them. This code block was inspired by
|
||
one you can find in Spacemacs.
|
||
#+name: org-hydra-babel
|
||
#+begin_src emacs-lisp :tangle no
|
||
(defhydra org-babel-transient ()
|
||
"
|
||
^Navigate^ ^Interact
|
||
^^^^^^^^^^^------------------------------------------
|
||
[_t_/_s_] navigate src blocs [_x_] execute src block
|
||
[_g_]^^ goto named block [_'_] edit src block
|
||
[_z_]^^ recenter screen [_q_] quit
|
||
"
|
||
("q" nil :exit t)
|
||
("t" org-babel-next-src-block)
|
||
("s" org-babel-previous-src-block)
|
||
("g" org-babel-goto-named-src-block)
|
||
("z" recenter-top-bottom)
|
||
("x" org-babel-execute-maybe)
|
||
("'" org-edit-special :exit t))
|
||
#+end_src
|
||
|
||
We next have keybindings related to org-mode’s agenda capabilities. We
|
||
can schedule a todo header for some dates, or set a deadline.
|
||
#+name: org-keybinds-dates
|
||
| Key chord | Function | Description |
|
||
|-----------+-------------------------+-------------|
|
||
| d | nil | dates |
|
||
| dd | org-deadline | |
|
||
| ds | org-schedule | |
|
||
| dt | org-time-stamp | |
|
||
| dT | org-time-stamp-inactive | |
|
||
|
||
Let’s now define some keybinds for inserting stuff in our org buffer:
|
||
#+name: org-keybinds-insert
|
||
| Key chord | Function | Description |
|
||
|-----------+-------------------------------+-------------|
|
||
| i | nil | insert |
|
||
| ib | org-insert-structure-template | |
|
||
| id | org-insert-drawer | |
|
||
| ie | org-set-effort | |
|
||
| if | org-footnote-new | |
|
||
| ih | org-insert-heading | |
|
||
| ii | org-insert-item | |
|
||
| il | org-insert-link | |
|
||
| in | org-add-note | |
|
||
| ip | org-set-property | |
|
||
| is | org-insert-subheading | |
|
||
| it | org-set-tags-command | |
|
||
|
||
There isn’t a lot of stuff I can jump to yet, but there’s still some:
|
||
#+name: org-keybinds-jump
|
||
| Key chord | Function | Description |
|
||
|-----------+----------------------+-------------|
|
||
| j | nil | jump |
|
||
| ja | counsel-org-goto-all | |
|
||
| jh | counsel-org-goto | |
|
||
|
||
Tables get a bit more love:
|
||
#+name: org-keybinds-tables
|
||
| Key chord | Function | Description |
|
||
|-----------+--------------------------------------+-------------|
|
||
| t | nil | tables |
|
||
| ta | org-table-align | |
|
||
| te | org-table-eval-formula | |
|
||
| tf | org-table-field-info | |
|
||
| th | org-table-convert | |
|
||
| tl | org-table-recalculate | |
|
||
| tp | org-plot/gnuplot | |
|
||
| ts | org-table-sort-lines | |
|
||
| tw | org-table-wrap-region | |
|
||
| tN | org-table-create-with-table.el | |
|
||
| tc | org-table-previous-field | |
|
||
| tr | org-table-next-field | |
|
||
| tC | org-table-move-column-left | |
|
||
| tT | org-table-move-row-down | |
|
||
| tS | org-table-move-row-up | |
|
||
| tR | org-table-move-column-right | |
|
||
| td | nil | delete |
|
||
| tdc | org-table-delete-column | |
|
||
| tdr | org-table-kill-row | |
|
||
| ti | nil | insert |
|
||
| tic | org-table-insert-column | |
|
||
| tih | org-table-insert-hline | |
|
||
| tir | org-table-insert-row | |
|
||
| tiH | org-table-hline-and-move | |
|
||
| tt | nil | toggle |
|
||
| ttf | org-table-toggle-formula-debugger | |
|
||
| tto | org-table-toggle-coordinate-overlays | |
|
||
|
||
Finally, let’s make enabling and disabling stuff accessible:
|
||
#+name: org-keybinds-toggles
|
||
| Key chord | Function | Description |
|
||
|-----------+--------------------------------------+-------------|
|
||
| T | nil | toggle |
|
||
| Tc | org-toggle-checkbox | |
|
||
| Ti | org-toggle-inline-images | |
|
||
| Tl | org-latex-preview | |
|
||
| Tn | org-num-mode | |
|
||
| Ts | phundrak/toggle-org-src-window-split | |
|
||
| Tt | org-show-todo-tree | |
|
||
| TT | org-todo | |
|
||
|
||
*** LaTeX formats
|
||
:PROPERTIES:
|
||
:header-args:emacs-lisp: :tangle no :exports code :results silent
|
||
:CUSTOM_ID: Packages-Configuration-Org-mode-LaTeX-formatszjf5fl6184j0
|
||
:END:
|
||
I currently have two custom formats for my Org-mode exports: one for general use
|
||
(initialy for my conlanging files, hence its ~conlang~ name), and one for beamer
|
||
exports.
|
||
|
||
Below is the declaration of the ~conlang~ LaTeX class:
|
||
#+NAME: org-latex-class-conlang
|
||
#+BEGIN_SRC emacs-lisp
|
||
'("conlang"
|
||
"\\documentclass{book}"
|
||
("\\chapter{%s}" . "\\chapter*{%s}")
|
||
("\\section{%s}" . "\\section*{%s}")
|
||
("\\subsection{%s}" . "\\subsection*{%s}")
|
||
("\\subsubsection{%s}" . "\\subsubsection*{%s}"))
|
||
#+END_SRC
|
||
|
||
And here is the declaration of the ~beamer~ class:
|
||
#+NAME: org-latex-class-beamer
|
||
#+BEGIN_SRC emacs-lisp
|
||
`("beamer"
|
||
,(concat "\\documentclass[presentation]{beamer}\n"
|
||
"[DEFAULT-PACKAGES]"
|
||
"[PACKAGES]"
|
||
"[EXTRA]\n")
|
||
("\\section{%s}" . "\\section*{%s}")
|
||
("\\subsection{%s}" . "\\subsection*{%s}")
|
||
("\\subsubsection{%s}" . "\\subsubsection*{%s}"))
|
||
#+END_SRC
|
||
|
||
Both these classes have to be added to ~org-latex-classes~ like so:
|
||
#+NAME: org-latex-classes
|
||
#+BEGIN_SRC emacs-lisp :noweb yes
|
||
(eval-after-load "ox-latex"
|
||
'(progn
|
||
(add-to-list 'org-latex-classes
|
||
<<org-latex-class-conlang>>)
|
||
(add-to-list 'org-latex-classes
|
||
<<org-latex-class-beamer>>)))
|
||
#+END_SRC
|
||
|
||
*** Projects
|
||
:PROPERTIES:
|
||
:header-args:emacs-lisp: :tangle no :exports code :results silent
|
||
:CUSTOM_ID: Packages-Configuration-Org-mode-Projectsf2h5fl6184j0
|
||
:END:
|
||
Another great features of Org-mode is the Org projects that allow the user to
|
||
easily publish a bunch of org files to a remote location. Here is the current
|
||
declaration of my projects, which will be detailed later:
|
||
#+NAME: org-publish-projects
|
||
#+BEGIN_SRC emacs-lisp :noweb yes
|
||
<<org-proj-config-setup>>
|
||
<<org-proj-lang-setup>>
|
||
(setq org-publish-project-alist
|
||
`(
|
||
<<org-proj-config-html>>
|
||
<<org-proj-config-static>>
|
||
<<org-proj-config>>
|
||
<<org-proj-lang-html>>
|
||
<<org-proj-lang-pdf>>
|
||
<<org-proj-lang-static>>
|
||
<<org-proj-lang>>))
|
||
#+END_SRC
|
||
|
||
**** Configuration website
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Org-mode-Projects-Configuration-websitelki5fl6184j0
|
||
:END:
|
||
This is my configuration for exporting my dotfiles to my website in a web format
|
||
only. No PDFs or anything, just HTML. Please note that I do not use that often
|
||
anymore, I much prefer the automatic script that I have which deploys through my
|
||
Drone instance my website on git pushes.
|
||
|
||
And before we get into the actual configuration, I would like to introduce a
|
||
couple of variables. This is a bit more verbose than if I declared everything
|
||
manually, but now I can change all three values at the same time without a
|
||
hasle.
|
||
#+NAME: org-proj-config-setup
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defvar phundrak//projects-config-target
|
||
"/rsync:Tilo:~/www/phundrak.com/config"
|
||
"Points to where exported files for config.phundrak.com should be put")
|
||
(defvar phundrak//projects-config-source
|
||
"~/org/config/"
|
||
"Points to where the sources for config.phundrak.com are")
|
||
(defvar phundrak//projects-config-language
|
||
"en"
|
||
"Language of config.phundrak.com")
|
||
(defvar phundrak//projects-config-recursive
|
||
t
|
||
"Defines whether subdirectories should be parsed for config.phundrak.com")
|
||
#+END_SRC
|
||
|
||
Now, here is my configuration. In this snippet, my org files located in my
|
||
source directory get exported in the HTML format and published to my target
|
||
directory on my remote server through RSYNC via TRAMP. A sitemap is
|
||
automatically generated, which comes in handy with the online sitemap that is
|
||
available through the navigation bar.
|
||
#+NAME: org-proj-config-html
|
||
#+BEGIN_SRC emacs-lisp
|
||
("config-website-org"
|
||
:base-directory ,phundrak//projects-config-source
|
||
:base-extension "org"
|
||
:publishing-directory ,phundrak//projects-config-target
|
||
:recursive ,phundrak//projects-config-recursive
|
||
:language ,phundrak//projects-config-language
|
||
:publishing-function org-html-publish-to-html
|
||
:headline-levels 5
|
||
:auto-sitemap t
|
||
:auto-preamble t)
|
||
#+END_SRC
|
||
|
||
We also have the component for all the static files needed to run the website
|
||
(mostly images tbh).
|
||
#+NAME: org-proj-config-static
|
||
#+BEGIN_SRC emacs-lisp
|
||
("config-website-static"
|
||
:base-directory ,phundrak//projects-config-source
|
||
:base-extension "png\\|jpg\\|gif\\|webp\\|svg\\|jpeg\\|ttf\\|woff\\|txt\\|epub\\|md"
|
||
:publishing-directory ,phundrak//projects-config-target
|
||
:recursive ,phundrak//projects-config-recursive
|
||
:language ,phundrak//projects-config-language
|
||
:publishing-function org-publish-attachment)
|
||
#+END_SRC
|
||
|
||
The project is then defined like so:
|
||
#+NAME: org-proj-config
|
||
#+BEGIN_SRC emacs-lisp
|
||
("config-website"
|
||
:components ("config-website-org"
|
||
"config-website-static"))
|
||
#+END_SRC
|
||
|
||
**** Linguistics website
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Org-mode-Projects-Linguistics-websitey4k5fl6184j0
|
||
:END:
|
||
My linguistics website is made out of three projects. As for the previous
|
||
project, let’s declare the common values for these.
|
||
#+NAME: org-proj-lang-setup
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defvar phundrak//projects-conlanging-target
|
||
"/rsync:Tilo:~/www/phundrak.com/langue/"
|
||
"Points to where exported files for langue.phundrak.com should be put")
|
||
(defvar phundrak//projects-conlanging-source
|
||
"~/Documents/conlanging/content/"
|
||
"Points to where the sources for langue.phundrak.com are")
|
||
(defvar phundrak//projects-conlanging-language
|
||
"fr"
|
||
"Language of langue.phundrak.com")
|
||
(defvar phundrak//projects-conlanging-recursive
|
||
t
|
||
"Defines whether subdirectories should be parsed for langue.phundrak.com")
|
||
#+END_SRC
|
||
|
||
The first component is the one generating the HTML files from the org files.
|
||
#+NAME: org-proj-lang-html
|
||
#+BEGIN_SRC emacs-lisp
|
||
("langue-phundrak-com-org"
|
||
:base-directory ,phundrak//projects-conlanging-source
|
||
:base-extension "org"
|
||
:exclude "\\./\\(CONTRIB\\|README\\|head\\|temp\\|svg-ink\\).*"
|
||
:publishing-directory ,phundrak//projects-conlanging-target
|
||
:recursive ,phundrak//projects-conlanging-recursive
|
||
:language ,phundrak//projects-conlanging-language
|
||
:publishing-function org-html-publish-to-html
|
||
:headline-levels 5
|
||
:auto-sitemap t
|
||
:auto-preamble t)
|
||
#+END_SRC
|
||
|
||
We also have the component for the LaTeX and PDF part of the website:
|
||
#+NAME: org-proj-lang-pdf
|
||
#+BEGIN_SRC emacs-lisp
|
||
("langue-phundrak-com-pdf"
|
||
:base-directory ,phundrak//projects-conlanging-source
|
||
:base-extension "org"
|
||
:exclude "\\./\\(CONTRIB\\|README\\|index\\|head\\|temp\\|svg-ink\\).*"
|
||
:publishing-directory ,phundrak//projects-conlanging-target
|
||
:recursive ,phundrak//projects-conlanging-recursive
|
||
:language ,phundrak//projects-conlanging-language
|
||
:publishing-function org-latex-publish-to-pdf
|
||
:headline-levels 5
|
||
:auto-preamble t)
|
||
#+END_SRC
|
||
|
||
And lastly, we have the component for all the static files needed to run the
|
||
website:
|
||
#+NAME: org-proj-lang-static
|
||
#+BEGIN_SRC emacs-lisp
|
||
("langue-phundrak-com-static"
|
||
:base-directory ,phundrak//projects-conlanging-source
|
||
:base-extension "png\\|jpg\\|gif\\|webp\\|svg\\|jpeg\\|ttf\\|woff\\|txt\\|epub"
|
||
:publishing-directory ,phundrak//projects-conlanging-target
|
||
:recursive ,phundrak//projects-conlanging-recursive
|
||
:language ,phundrak//projects-conlanging-language
|
||
:publishing-function org-publish-attachment)
|
||
#+END_SRC
|
||
|
||
The project is then defined like so:
|
||
#+NAME: org-proj-lang
|
||
#+BEGIN_SRC emacs-lisp
|
||
("langue-phundrak-com"
|
||
:components ("langue-phundrak-com-org"
|
||
"langue-phundrak-com-static"
|
||
"langue-phundrak-com-pdf"))
|
||
#+END_SRC
|
||
|
||
*** Visual Configuration
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Org-mode-Visual-Configurationrol5fl6184j0
|
||
:END:
|
||
While most modes of Emacs are dedicated to development, and therefore
|
||
are much more comfortable with a fixed-pitch font, more literary modes
|
||
such as org-mode are much more enjoyable if you have a variable pitch
|
||
font enabled. *BUT*, these modes can also require some fixed-pitch fonts
|
||
for some elements of the buffer, such as code blocks with
|
||
org-mode. ~mixed-pitch~ comes to the rescue!
|
||
#+begin_src emacs-lisp
|
||
(use-package mixed-pitch
|
||
:after org
|
||
:straight t
|
||
:hook
|
||
(org-mode . mixed-pitch-mode)
|
||
:config
|
||
(add-hook 'org-agenda-mode-hook (lambda () (mixed-pitch-mode -1))))
|
||
#+end_src
|
||
|
||
With this, I also use ~org-pretty-tables~ in order to use some
|
||
capabilities of Unicode in order to make tables nicer to look at.
|
||
#+begin_src emacs-lisp
|
||
(use-package org-pretty-table
|
||
:defer t
|
||
:after org
|
||
:straight (org-pretty-table :type git
|
||
:host github
|
||
:repo "Fuco1/org-pretty-table"
|
||
:build t)
|
||
:hook (org-mode . org-pretty-table-mode)
|
||
:commands (org-pretty-table-mode global-org-pretty-table-mode))
|
||
#+end_src
|
||
|
||
I have an issue with org-mode’s emphasis markers: I find them ugly. I
|
||
can of course hide them if I simply set ~org-hide-emphasis-markers~ to
|
||
~t~, but it makes editing hard since I never know whether I am before or
|
||
after the emphasis marker when editing near the beginning/end of an
|
||
emphasized region. ~org-appear~ fixes this issue so that it shows the
|
||
emphasis markers only when the cursor is in the emphasized region,
|
||
otherwise they will remain hidden! Very cool!
|
||
#+begin_src emacs-lisp
|
||
(use-package org-appear
|
||
:after org
|
||
:straight (:build t)
|
||
:hook (org-mode . org-appear-mode)
|
||
:config
|
||
(setq org-appear-autoemphasis t
|
||
org-hide-emphasis-markers t
|
||
org-appear-autolinks t
|
||
org-appear-autoentities t
|
||
org-appear-autosubmarkers t)
|
||
(run-at-time nil nil #'org-appear--set-elements))
|
||
#+end_src
|
||
|
||
Similarly, LaTeX fragments previews are nice and all, but if I have my
|
||
cursor on it, I want to see the LaTeX source code and modify it, not
|
||
just the generated image!
|
||
#+begin_src emacs-lisp
|
||
(use-package org-fragtog
|
||
:defer t
|
||
:after org
|
||
:straight (:build t)
|
||
:hook (org-mode . org-fragtog-mode))
|
||
#+end_src
|
||
|
||
Tired of seeing lots of actual stars ~*~ in your headers, and you want a
|
||
fancier remplacement? Or you are still using ~org-bullets~ but just
|
||
found out how out-of-date and abandoned it is? (Last commit was on
|
||
September 18th, 2014… damn…) Search no more, ~org-superstar~ will take
|
||
care of that for you!
|
||
#+begin_src emacs-lisp
|
||
(use-package org-superstar
|
||
:after org
|
||
:straight (:build t)
|
||
:hook (org-mode . org-superstar-mode)
|
||
:config
|
||
(setq org-superstar-leading-bullet ?\s
|
||
org-superstar-leading-fallback ?\s
|
||
org-hide-leading-stars nil
|
||
org-superstar-todo-bullet-alist
|
||
'(("TODO" . 9744)
|
||
("[ ]" . 9744)
|
||
("DONE" . 9745)
|
||
("[X]" . 9745))))
|
||
#+end_src
|
||
~org-fancy-priorities~ change the priority of an org element such such
|
||
as ~#A~ to anything user-defined. Let’s make this anything all-the-icons
|
||
icons!
|
||
#+begin_src emacs-lisp
|
||
(use-package org-fancy-priorities
|
||
:after (org all-the-icons)
|
||
:straight (:build t)
|
||
:hook (org-mode . org-fancy-priorities-mode)
|
||
:hook (org-agenda-mode . org-fancy-priorities-mode)
|
||
:config
|
||
(setq org-fancy-priorities-list `(,(all-the-icons-faicon "flag" :height 1.1 :v-adjust 0.0)
|
||
,(all-the-icons-faicon "arrow-up" :height 1.1 :v-adjust 0.0)
|
||
,(all-the-icons-faicon "square" :height 1.1 :v-adjust 0.0))))
|
||
#+end_src
|
||
/Org Outline Tree/ is a better way of managing my org files’ outline.
|
||
#+begin_src emacs-lisp
|
||
(use-package org-ol-tree
|
||
:defer t
|
||
:after org
|
||
:straight (org-ol-tree :build t
|
||
:host github
|
||
:type git
|
||
:repo "Townk/org-ol-tree")
|
||
:general
|
||
(:keymaps 'org-mode-map
|
||
:states 'normal
|
||
:prefix ","
|
||
"O" #'org-ol-tree))
|
||
#+end_src
|
||
|
||
#+RESULTS:
|
||
|
||
** Programming languages
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Programming-languages6et5fl6184j0
|
||
:END:
|
||
*** DSLs
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Programming-languages-DSLsbwu5fl6184j0
|
||
:END:
|
||
DSLs, or /Domain Specific Languages/, are languages dedicated to some
|
||
very tasks, such as configuration languages or non-general programming
|
||
such as SQL.
|
||
|
||
**** Caddy
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Programming-languages-DSLs-Caddy0fw5fl6184j0
|
||
:END:
|
||
[[https://caddyserver.com/][Caddy]] (or /Caddyserver/) is a web server akin to Nginx or Apache which I
|
||
find much easier to configure that the latter two, plus it has
|
||
built-in support for automatically generating SSL certificates with
|
||
Letsencrypt! Automatic HTTPS, what more do you want?
|
||
|
||
All that is nice and all, but Emacs doesn’t support the syntax of
|
||
Caddy files natively, so let’s install ~caddyfile-mode~:
|
||
#+begin_src emacs-lisp
|
||
(use-package caddyfile-mode
|
||
:defer t
|
||
:straight (:build t)
|
||
:mode (("Caddyfile\\'" . caddyfile-mode)
|
||
("caddy\\.conf\\'" . caddyfile-mode)))
|
||
#+end_src
|
||
|
||
**** Gnuplot
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Programming-languages-DSLs-Gnuplot8zx5fl6184j0
|
||
:END:
|
||
This package is a front-end and major mode for the programming
|
||
language [[http://www.gnuplot.info/][Gnuplot]]. Let’s make some beautiful graphs, shall we?
|
||
#+begin_src emacs-lisp
|
||
(use-package gnuplot
|
||
:straight (:build t)
|
||
:defer t)
|
||
#+end_src
|
||
|
||
**** Nginx
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Programming-languages-DSLs-Nginxjiz5fl6184j0
|
||
:END:
|
||
Nginx is another webserver, older and more mature than Caddy. A couple
|
||
of packages are required in order to be able to properly work with
|
||
Nginx configuration files. First, we need the correct mode for editing
|
||
Nginx configuration files.
|
||
#+begin_src emacs-lisp
|
||
(use-package nginx-mode
|
||
:straight (:build t)
|
||
:defer t)
|
||
#+end_src
|
||
|
||
We then also have an autocompletion package that adds to ~company~ the
|
||
Nginx syntax.
|
||
#+begin_src emacs-lisp
|
||
(use-package company-nginx
|
||
:straight (:build t)
|
||
:defer t
|
||
:config
|
||
(add-hook 'nginx-mode-hook (lambda ()
|
||
(add-to-list 'company-backends #'company-nginx))))
|
||
#+end_src
|
||
|
||
**** Shells
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Programming-languages-DSLs-Shellsn116fl6184j0
|
||
:END:
|
||
Aside from Eshell, my main shell on my machine is fish (see my [[file:fish.org][fish
|
||
config]]), therefore I need a mode for it.
|
||
#+begin_src emacs-lisp
|
||
(use-package fish-mode
|
||
:straight (:build t)
|
||
:defer t)
|
||
#+end_src
|
||
|
||
**** Yaml
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Programming-languages-DSLs-Yamlsk26fl6184j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
(use-package yaml-mode
|
||
:defer t
|
||
:straight (:build t)
|
||
:mode "\\.yml\\'"
|
||
:mode "\\.yaml\\'")
|
||
#+end_src
|
||
|
||
*** Flycheck
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Programming-languages-Flycheckb446fl6184j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
(use-package flycheck
|
||
:straight (:build t)
|
||
:defer t
|
||
:init
|
||
(global-flycheck-mode)
|
||
:config
|
||
(setq flycheck-emacs-lisp-load-path 'inherit)
|
||
|
||
;; Rerunning checks on every newline is a mote excessive.
|
||
(delq 'new-line flycheck-check-syntax-automatically)
|
||
;; And don’t recheck on idle as often
|
||
(setq flycheck-idle-change-delay 2.0)
|
||
|
||
;; For the above functionality, check syntax in a buffer that you
|
||
;; switched to on briefly. This allows “refreshing” the syntax check
|
||
;; state for several buffers quickly after e.g. changing a config
|
||
;; file.
|
||
(setq flycheck-buffer-switch-check-intermediate-buffers t)
|
||
|
||
;; Display errors a little quicker (default is 0.9s)
|
||
(setq flycheck-display-errors-delay 0.2))
|
||
|
||
(use-package flycheck-popup-tip
|
||
:straight (:build t)
|
||
:after flycheck
|
||
:hook (flycheck-mode . flycheck-popup-tip-mode)
|
||
:config
|
||
(setq flycheck-popup-tip-error-prefix "X ")
|
||
(after! evil
|
||
(add-hook 'evil-insert-state-entry-hook
|
||
#'flycheck-popup-tip-delete-popup)
|
||
(add-hook 'evil-replace-state-entry-hook
|
||
#'flycheck-popup-tip-delete-popup)))
|
||
|
||
(use-package flycheck-posframe
|
||
:straight (:build t)
|
||
:hook (flycheck-mode . flycheck-posframe-mode)
|
||
:config
|
||
(setq flycheck-posframe-warning-prefix "! "
|
||
flycheck-posframe-info-prefix "··· "
|
||
flycheck-posframe-error-prefix "X "))
|
||
#+end_src
|
||
|
||
*** General Programming Languages
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Programming-languages-General-Programming-Languageszn56fl6184j0
|
||
:END:
|
||
**** EmacsLisp
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Programming-languages-General-Programming-Languages-EmacsLispo876fl6184j0
|
||
:END:
|
||
This package displays the function’s arglist or variable’s docstring
|
||
in the echo area at the bottom of the frame. Quite useful indeed.
|
||
#+begin_src emacs-lisp
|
||
(use-package eldoc
|
||
:defer t
|
||
:after company
|
||
:init
|
||
(eldoc-add-command 'company-complete-selection
|
||
'company-complete-common
|
||
'company-capf
|
||
'company-abort))
|
||
#+end_src
|
||
|
||
Let’s also declare some Elisp-dedicated keybindings, prefixed by a
|
||
comma.
|
||
#+begin_src emacs-lisp
|
||
(general-define-key
|
||
:states 'motion
|
||
:keymaps 'emacs-lisp-mode-map
|
||
:prefix ","
|
||
|
||
"'" #'ielm
|
||
"c" '(emacs-lisp-byte-compile :which-key "Byte compile")
|
||
|
||
"e" '(nil :which-key "eval")
|
||
"eb" #'eval-buffer
|
||
"ed" #'eval-defun
|
||
"ee" #'eval-last-sexp
|
||
"er" #'eval-region
|
||
|
||
"h" '(nil :which-key "help")
|
||
"hh" #'helpful-at-point
|
||
|
||
"t" '(nil :wk "toggle")
|
||
"tP" '(nil :wk "parinfer")
|
||
"tPs" #'parinfer-rust-switch-mode
|
||
"tPd" #'parinfer-rust-mode-disable
|
||
"tPp" #'parinfer-rust-toggle-paren-mode)
|
||
#+end_src
|
||
|
||
** Visual Configuration
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Visual-Configurationxr86fl6184j0
|
||
:END:
|
||
*** Dashboard
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Visual-Configuration-Dashboardnba6fl6184j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
(use-package dashboard
|
||
:straight (:build t)
|
||
:ensure t
|
||
:after all-the-icons
|
||
:config
|
||
(setq dashboard-banner-logo-title "Phundrak’s Vanilla Emacs"
|
||
dashboard-startup-banner 'logo
|
||
dashboard-center-content t
|
||
dashboard-show-shortcuts t
|
||
dashboard-set-navigator t
|
||
dashboard-set-heading-icons t
|
||
dashboard-set-file-icons t
|
||
initial-buffer-choice (lambda () (get-buffer "*dashboard*"))
|
||
dashboard-projects-switch-function 'counsel-projectile-switch-project-by-name)
|
||
(setq dashboard-navigator-buttons
|
||
`(((,(all-the-icons-faicon "language" :height 1.1 :v-adjust 0.0)
|
||
"Linguistics website"
|
||
""
|
||
(lambda (&rest _) (browse-url "https://langue.phundrak.com")))
|
||
|
||
(,(all-the-icons-faicon "firefox" :height 1.1 :v-adjust 0.0)
|
||
"Config Website"
|
||
""
|
||
(lambda (&rest _) (browse-url "https://config.phundrak.com"))))
|
||
|
||
((,(all-the-icons-octicon "git-branch" :height 1.1 :v-adjust 0.0)
|
||
"Dotfiles sources"
|
||
""
|
||
(lambda (&rest _) (browse-url "https://labs.phundrak.com/phundrak/dotfiles")))
|
||
("!" "Issues" "Show issues" (lambda (&rest _)
|
||
(browse-url "https://labs.phundrak.com/phundrak/dotfiles/issues"))
|
||
warning))
|
||
((,(all-the-icons-faicon "level-up" :height 1.1 :v-adjust 0.0)
|
||
"Update packages"
|
||
""
|
||
(lambda (&rest _) (progn
|
||
(require 'straight)
|
||
(straight-pull-all)))))))
|
||
|
||
(setq dashboard-items '((recents . 15)
|
||
(projects . 10)))
|
||
(dashboard-setup-startup-hook)
|
||
:init
|
||
(add-hook 'after-init-hook 'dashboard-refresh-buffer))
|
||
#+end_src
|
||
|
||
*** Modeline
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Visual-Configuration-Modelineavb6fl6184j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
(use-package doom-modeline
|
||
:straight (:build t)
|
||
:defer t
|
||
:init (doom-modeline-mode 1)
|
||
:custom ((doom-modeline-height 15)))
|
||
#+end_src
|
||
|
||
*** Theme
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Visual-Configuration-Themeded6fl6184j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
(use-package doom-themes
|
||
:straight (:build t)
|
||
:defer t
|
||
:init (load-theme 'doom-nord t))
|
||
#+end_src
|
||
|
||
*** Icons? Did someone say icons?
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Visual-Configuration-Icons-Did-someone-say-iconsfye6fl6184j0
|
||
:END:
|
||
/*YES! ALL OF THEM!*/
|
||
|
||
Ahem…
|
||
|
||
The package ~all-the-icons~ allows us to use a wide variety of icons in
|
||
Emacs for various purposes, wherever we want, and /THAT/ is *GREAT*! I’ll
|
||
(ab)use this feature in my config, be warned! *NOTE*: The first time a
|
||
configuration with ~all-the-icons~ is loaded on a machine, the needed
|
||
fonts might not be available, so you’ll need to install them with the
|
||
command ~M-x all-the-icons-install-fonts~.
|
||
#+begin_src emacs-lisp
|
||
(use-package all-the-icons
|
||
:defer t)
|
||
#+end_src
|
||
~prettify-symbols-mode~ is also a nifty feature of Emacs, and it is
|
||
built-in! With that, I can replace strings of my choice by another
|
||
character of my choice!
|
||
#+begin_src emacs-lisp
|
||
(dolist (symbol '(("lambda" . 955)
|
||
("mapc" . 8614)))
|
||
(add-to-list 'prettify-symbols-alist symbol))
|
||
#+end_src
|
||
|
||
Let’s enable this mode for any programming mode:
|
||
#+begin_src emacs-lisp
|
||
(add-hook 'emacs-lisp-mode-hook #'prettify-symbols-mode)
|
||
#+end_src
|
||
|
||
*** Rainbow Delimiters
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Visual-Configuration-Rainbow-Delimiters3lg6fl6184j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
(use-package rainbow-delimiters
|
||
:straight (:build t)
|
||
:defer t
|
||
:hook (prog-mode . rainbow-delimiters-mode))
|
||
#+end_src
|
||
|
||
*** Y’all want some more /COLORS/?
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Visual-Configuration-Y-all-want-some-more-COLORSs6i6fl6184j0
|
||
:END:
|
||
It is possible to make info buffers much more colorful (and imo easier to read) with this simple package:
|
||
#+begin_src emacs-lisp
|
||
(use-package info-colors
|
||
:straight (:build t)
|
||
:commands info-colors-fnontify-node
|
||
:hook (Info-selection . info-colors-fontify-node)
|
||
:hook (Info-mode . mixed-pitch-mode))
|
||
#+end_src
|
||
|
||
** Misc
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Misc0sj6fl6184j0
|
||
:END:
|
||
*** ~avy~
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Misc-avyral6fl6184j0
|
||
:END:
|
||
~avy~ is a really convenient way of jumping around, but I’ll need some
|
||
configuration to make it bépo-compatible.
|
||
#+begin_src emacs-lisp
|
||
(use-package avy
|
||
:defer t
|
||
:config
|
||
(setq avy-keys '(?a ?u ?i ?e ?c ?t ?s ?r ?n))
|
||
:general
|
||
(:states 'normal
|
||
"gl" #'avy-goto-line))
|
||
#+end_src
|
||
|
||
*** Calc
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Misc-Calc3vm6fl6184j0
|
||
:END:
|
||
Let’s give ~calc-mode~ some better defaults.
|
||
#+begin_src emacs-lisp
|
||
(setq calc-angle-mode 'rad
|
||
calc-symbolic-mode t)
|
||
#+end_src
|
||
|
||
*** Elcord
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Misc-Elcord7eo6fl6184j0
|
||
:END:
|
||
What’s the point of using Emacs if you can’t tell everyone?
|
||
#+begin_src emacs-lisp
|
||
(use-package elcord
|
||
:straight (:built t)
|
||
:defer t
|
||
:config
|
||
(setq elcord-use-major-mode-as-main-icon t
|
||
elcord-refresh-rate 5
|
||
elcord-display-elapsed nil))
|
||
#+end_src
|
||
|
||
*** ~ivy-quick-find-files.el~
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Misc-ivy-quick-find-files-el2yp6fl6184j0
|
||
:END:
|
||
This package is a small utility package I’ve written in order to
|
||
quickly find files across my filesystem.
|
||
#+begin_src emacs-lisp
|
||
(use-package ivy-quick-find-files
|
||
:defer t
|
||
:straight (ivy-quick-find-files :type git
|
||
:host github
|
||
:repo "phundrak/ivy-quick-find-files.el"
|
||
:build t)
|
||
:config
|
||
(setq ivy-quick-find-files-program 'fd
|
||
ivy-quick-find-files-dirs-and-exts '(("~/org" . "org")
|
||
("~/Documents/conlanging" . "org")
|
||
("~/Documents/university" . "org"))))
|
||
#+end_src
|
||
|
||
*** Winum
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Misc-Winumvir6fl6184j0
|
||
:END:
|
||
Winum allows Emacs to associate windows with a specific number and
|
||
navigate through these windows by directly refering to their
|
||
associated number! This allows for faster window configuration than
|
||
just going to the frame above, then left, left, and up.
|
||
#+begin_src emacs-lisp
|
||
(use-package winum
|
||
:straight (:build t)
|
||
:init (winum-mode))
|
||
#+end_src
|
||
|
||
* Keybindings
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Keybindings3ps6fl6184j0
|
||
:END:
|
||
Undefining some stuff to make keybind prefixes work correctly.
|
||
#+begin_src emacs-lisp
|
||
(general-define-key
|
||
:keymaps '(backtrace-mode-map diff-minor-mode-map magit-mode-map
|
||
rmail-mode-map evil-motion-state-map dired-mode-map
|
||
epa-key-list-mode-map special-mode-map splash-screen-keymap
|
||
undo-tree-visualizer-mode-map magit-blame-read-only-mode-map
|
||
org-agenda-keymap org-agenda-mode-map git-rebase-mode-map
|
||
Buffer-menu-mode-map custom-mode-map gfm-view-mode-map
|
||
electric-help-map image-mode-map magit-diff-mode-map)
|
||
"SPC" nil)
|
||
|
||
(general-define-key
|
||
:keymaps 'evil-motion-state-map
|
||
"," nil)
|
||
|
||
(general-define-key
|
||
:keymaps 'evil-insert-state-map
|
||
"C-t" nil)
|
||
|
||
(general-define-key
|
||
:keymaps '(diff-mode-map help-mode-map image-mode-map
|
||
dired-mode-map Man-mode-map eww-mode-map magit-mode-map
|
||
debugger-mode-map dired-mode-map custom-mode-map
|
||
eshell-mode-map)
|
||
:states 'normal
|
||
"SPC" nil)
|
||
|
||
(general-define-key
|
||
:keymaps 'magit-mode-map
|
||
:states 'visual
|
||
"SPC" nil)
|
||
|
||
(general-define-key
|
||
:keymaps '(diff-mode-map org-agenda-keymap org-agenda-mode-map)
|
||
:states 'motion
|
||
"SPC" nil)
|
||
|
||
(general-define-key
|
||
:keymaps 'eshell-mode-map
|
||
:states 'normal
|
||
"c" #'evil-backward-char
|
||
"t" #'evil-next-line
|
||
"s" #'evil-previous-line
|
||
"r" #'evil-forward-char)
|
||
|
||
(general-define-key
|
||
:keymaps 'evil-insert-state-map
|
||
"U" nil
|
||
"C-a" nil
|
||
"C-y" nil
|
||
"C-e" nil)
|
||
|
||
(general-define-key
|
||
:states 'normal
|
||
"U" #'evil-redo
|
||
"C-a" #'beginning-of-line
|
||
"C-e" #'end-of-line
|
||
"C-y" #'yank)
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(general-define-key
|
||
:states 'normal
|
||
:prefix "SPC"
|
||
"SPC" '(counsel-M-x :wk "M-x")
|
||
"'" #'shell-pop
|
||
|
||
"a" '(nil :wk "apps")
|
||
"ac" #'calc
|
||
"ad" #'docker
|
||
"ae" #'eww
|
||
"at" #'tetris
|
||
"aw" #'wttrin
|
||
"aC" #'calendar
|
||
|
||
"as" '(nil :wk "shells")
|
||
"ase" #'eshell-new
|
||
"asv" #'vterm
|
||
|
||
"b" '(nil :wk "buffers")
|
||
"bb" #'bufler-switch-buffer
|
||
"bB" #'bury-buffer
|
||
"bl" #'bufler
|
||
"bd" #'kill-this-buffer
|
||
"bD" #'kill-buffer
|
||
"bh" #'dashboard-refresh-buffer
|
||
"bm" #'switch-to-messages-buffer
|
||
"br" #'counsel-buffer-or-recentf
|
||
"bs" #'switch-to-scratch-buffer
|
||
|
||
"c" '(nil :wk "code")
|
||
"cl" #'evilnc-comment-or-uncomment-lines
|
||
|
||
"e" '(nil :wk "email")
|
||
"ec" #'mu4e-compose-new
|
||
"em" #'mu4e
|
||
|
||
"f" '(nil :wk "files")
|
||
"fc" '((lambda ()
|
||
(interactive)
|
||
(find-file (concat (getenv "HOME") "/org/config/emacs.org")))
|
||
:wk "Config file")
|
||
"ff" #'counsel-find-file
|
||
"fF" #'ivy-quick-find-files
|
||
"fh" #'hexl-find-file
|
||
"fr" #'counsel-recentf
|
||
"fs" #'save-buffer
|
||
|
||
"h" '(nil :wk "help")
|
||
"hk" #'which-key-show-top-level
|
||
"hd" '(nil :wk "describe")
|
||
"hdc" #'describe-char
|
||
"hdC" #'helpful-command
|
||
"hdf" #'helpful-callable
|
||
"hdk" #'helpful-key
|
||
"hdm" #'helpful-macro
|
||
"hdM" #'helpful-mode
|
||
"hds" #'helpful-symbol
|
||
"hdv" #'helpful-variable
|
||
|
||
"i" '(nil :wk "insert")
|
||
"iy" #'ivy-yasnippet
|
||
|
||
"j" '(nil :wk "jump")
|
||
"jd" #'dired-jump
|
||
"jD" #'dired-jump-other-window
|
||
|
||
"p" '(nil :wk "project")
|
||
"p!" #'projectile-run-shell-command-in-root
|
||
"p&" #'projectile-run-async-shell-command-in-root
|
||
"pb" #'counsel-projectile-switch-to-buffer
|
||
"pc" #'counsel-projectile
|
||
"pd" #'counsel-projectile-find-dir
|
||
"pe" #'projectile-edit-dir-locals
|
||
"pf" #'counsel-projectile-find-file
|
||
"pg" #'projectile-find-tag
|
||
"pk" #'project-kill-buffers
|
||
"pp" #'counsel-projectile-switch-project
|
||
"pt" #'ivy-magit-todos
|
||
"pv" #'projectile-vc
|
||
|
||
"t" '(nil :wk "toggles")
|
||
"tt" #'counsel-load-theme
|
||
"ti" '(nil :wk "input method")
|
||
"tit" #'toggle-input-method
|
||
"tis" #'set-input-mode
|
||
|
||
"u" #'universal-argument
|
||
"U" #'undo-tree-visualize
|
||
|
||
"w" '(nil :wk "windows")
|
||
"w." #'windows-adjust-size/body
|
||
"w-" #'split-window-below-and-focus
|
||
"w/" #'split-window-right-and-focus
|
||
"w$" #'winum-select-window-by-number
|
||
"w0" '(winum-select-window-0-or-10 :wk nil)
|
||
"w1" '(winum-select-window-1 :wk nil)
|
||
"w2" '(winum-select-window-2 :wk nil)
|
||
"w3" '(winum-select-window-3 :wk nil)
|
||
"w4" '(winum-select-window-4 :wk nil)
|
||
"w5" '(winum-select-window-5 :wk nil)
|
||
"w6" '(winum-select-window-6 :wk nil)
|
||
"w7" '(winum-select-window-7 :wk nil)
|
||
"w8" '(winum-select-window-8 :wk nil)
|
||
"w9" '(winum-select-window-9 :wk nil)
|
||
|
||
"wb" '((lambda ()
|
||
(interactive)
|
||
(progn
|
||
(kill-this-buffer)
|
||
(delete-window)))
|
||
:wk "Kill buffer and window")
|
||
"wd" #'delete-window
|
||
"wo" #'other-window
|
||
"wD" #'delete-other-windows
|
||
"ww" '(nil :wk "writeroom")
|
||
"ww." #'writeroom-buffer-width/body
|
||
"www" #'writeroom-mode
|
||
|
||
"wc" #'evil-window-left
|
||
"wt" #'evil-window-down
|
||
"ws" #'evil-window-up
|
||
"wr" #'evil-window-right
|
||
|
||
"T" '(nil :wk "text")
|
||
"Tz" #'hydra-zoom/body
|
||
"Tu" #'downcase-region
|
||
"TU" #'upcase-region
|
||
"Te" #'string-edit-at-point
|
||
|
||
"q" '(nil :wk "quit")
|
||
"qf" #'delete-frame
|
||
"qq" #'save-buffers-kill-terminal
|
||
"qQ" #'kill-emacs)
|
||
#+end_src
|
||
|
||
* Various TODOs :noexport:
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Various-TODOsnwt6fl6184j0
|
||
:END:
|
||
** TODO advise ~evil-insert~ in eshell
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Various-TODOs-advise-evil-insert-in-eshellc4v6fl6184j0
|
||
:END:
|
||
Advise ~evil-insert~ to go to the end of the buffer while in
|
||
~eshell-mode~.
|
||
** TODO Write macro wrapper around ~general~
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Various-TODOs-Write-macro-wrapper-around-generalfew6fl6184j0
|
||
:END:
|
||
Write a macro wrapper around ~general~ for when an evil state is used in
|
||
order to make the keybind available when in ~insert-mode~ through ~M-m~.
|