5889 lines
202 KiB
Org Mode
5889 lines
202 KiB
Org Mode
#+title: Emacs Configuration
|
||
#+setupfile: 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.d/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.
|
||
#+headers: :exports code :results silent :lexical t
|
||
#+begin_src emacs-lisp :mkdirp yes :tangle ~/.emacs.d/early-init.el
|
||
(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-default 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
|
||
|
||
**** 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
|
||
|
||
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
|
||
|
||
I’m also interested in keeping track of how fast Emacs loads on my
|
||
computer, as well as for how long I keep it open. The values are
|
||
stored in a ~.csv~ with three columns:
|
||
- The date when Emacs was closed
|
||
- Emacs’ init time
|
||
- Emacs’ uptime
|
||
#+begin_src emacs-lisp
|
||
(defvar my/emacs-log-file-time (expand-file-name "emacs-time.csv" user-emacs-directory))
|
||
|
||
(defun my/write-emacs-loadtime-and-uptime ()
|
||
(with-temp-buffer
|
||
(find-file my/emacs-log-file-time)
|
||
(goto-char (point-max))
|
||
(insert (format "%s,%s,%s\n"
|
||
(string-trim (shell-command-to-string "date +%F-%H-%M-%S"))
|
||
(emacs-init-time "%f")
|
||
(emacs-uptime "%dd %hh %mm %ss")))
|
||
(save-buffer)))
|
||
|
||
(add-to-list 'kill-emacs-hook #'my/write-emacs-loadtime-and-uptime)
|
||
#+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
|
||
|
||
Thanks to [[https://github.com/TheVaffel/emacs][this fork]] of Emacs, it is now possible to set some
|
||
transparency to the background of Emacs only and not to the entire
|
||
frame.
|
||
#+begin_src emacs-lisp
|
||
(add-hook 'server-after-make-frame-hook (lambda ()
|
||
(interactive)
|
||
(set-frame-parameter (selected-frame)
|
||
'alpha-background 0.9)))
|
||
|
||
#+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-lib)
|
||
(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-buffer)))
|
||
|
||
(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 ()
|
||
"Spawn a new window right of the current one and focus it."
|
||
(interactive)
|
||
(split-window-right)
|
||
(windmove-right))
|
||
|
||
(defun split-window-below-and-focus ()
|
||
"Spawn a new window below the current one and focus it."
|
||
(interactive)
|
||
(split-window-below)
|
||
(windmove-down))
|
||
|
||
(defun kill-buffer-and-delete-window ()
|
||
"Kill the current buffer and delete its window."
|
||
(interactive)
|
||
(progn
|
||
(kill-this-buffer)
|
||
(delete-window)))
|
||
#+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,
|
||
all keybinds will be prefixed with ~SPC~ and keybinds related to a
|
||
specific mode (often major modes) will be prefixed by a comma ~,~ (and
|
||
by ~C-SPC~ and ~M-m~ respectively when in ~insert-mode~ or ~emacs-mode~). You
|
||
can still feel some influence from my Spacemacs years here.
|
||
#+begin_src emacs-lisp
|
||
(use-package general
|
||
:defer t
|
||
:straight (:build t)
|
||
:init
|
||
(general-auto-unbind-keys)
|
||
:config
|
||
(general-create-definer phundrak/undefine
|
||
:keymaps 'override
|
||
:states '(normal emacs))
|
||
(general-create-definer phundrak/evil
|
||
:states '(normal))
|
||
(general-create-definer phundrak/leader-key
|
||
:states '(normal insert visual emacs)
|
||
:keymaps 'override
|
||
:prefix "SPC"
|
||
:global-prefix "C-SPC")
|
||
(general-create-definer phundrak/major-leader-key
|
||
:states '(normal insert visual emacs)
|
||
:keymaps 'override
|
||
:prefix ","
|
||
:global-prefix "M-m"))
|
||
#+end_src
|
||
|
||
#+name: general-keybindings-gen
|
||
#+headers: :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))
|
||
(function (if (or (string= "nil" function)
|
||
(string= "" function))
|
||
":ignore"
|
||
function))
|
||
(comment (caddr line)))
|
||
(format "\"%s\" %s" key
|
||
(if (string= "" comment)
|
||
(if (string= "nil" function)
|
||
"nil"
|
||
(concat "#'" function))
|
||
(format "'(%s :which-key \"%s\")"
|
||
function
|
||
(if (string= "" function)
|
||
"nil"
|
||
comment))))))
|
||
table
|
||
"\n")
|
||
#+end_src
|
||
|
||
** 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
|
||
(general-define-key
|
||
:keymaps 'evil-motion-state-map
|
||
"SPC" nil
|
||
"," nil)
|
||
(general-define-key
|
||
:keymaps 'evil-insert-state-map
|
||
"C-t" nil)
|
||
(general-define-key
|
||
:keymaps 'evil-insert-state-map
|
||
"U" nil
|
||
"C-a" nil
|
||
"C-y" nil
|
||
"C-e" nil)
|
||
(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)
|
||
|
||
(dolist (key '("c" "C" "t" "T" "s" "S" "r" "R" "h" "H" "j" "J" "k" "K" "l" "L"))
|
||
(general-define-key :states 'normal key nil))
|
||
|
||
(general-define-key
|
||
:states 'motion
|
||
"h" 'evil-replace
|
||
"H" 'evil-replace-state
|
||
"j" 'evil-find-char-to
|
||
"J" 'evil-find-char-to-backward
|
||
"k" 'evil-substitute
|
||
"K" 'evil-smart-doc-lookup
|
||
"l" 'evil-change
|
||
"L" 'evil-change-line
|
||
|
||
"c" 'evil-backward-char
|
||
"C" 'evil-window-top
|
||
"t" 'evil-next-line
|
||
"T" 'evil-join
|
||
"s" 'evil-previous-line
|
||
"S" 'evil-lookup
|
||
"r" 'evil-forward-char
|
||
"R" 'evil-window-bottom))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(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-horizontally)
|
||
("t" enlarge-window)
|
||
("s" shrink-window)
|
||
("r" enlarge-window-horizontally))
|
||
#+end_src
|
||
|
||
* Packages Configuration
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configurationije0fl6184j0
|
||
:END:
|
||
** 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-commit 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))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package company-dict
|
||
:after company
|
||
:straight (:build t)
|
||
:config
|
||
(setq company-dict-dir (expand-file-name "dicts" user-emacs-directory)))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(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)
|
||
("C-u" . ivy-scroll-up-command)
|
||
("C-d" . ivy-scroll-down-command)
|
||
: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
|
||
:after (:any ivy helpful)
|
||
:hook (ivy-mode . ivy-posframe-mode)
|
||
:straight (:build t)
|
||
:init
|
||
(ivy-posframe-mode 1)
|
||
: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
|
||
:after ivy
|
||
: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)
|
||
:hook ((prog-mode . yas-minor-mode)
|
||
(text-mode . yas-minor-mode)))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package yasnippet-snippets
|
||
:defer t
|
||
:after yasnippet
|
||
:straight (:build t))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package yatemplate
|
||
:defer t
|
||
:after yasnippet
|
||
:straight (:build t))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package ivy-yasnippet
|
||
:defer t
|
||
:after (ivy yasnippet)
|
||
:straight (:build t))
|
||
#+end_src
|
||
|
||
** Applications
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Applications94i0fl6184j0
|
||
:END:
|
||
*** Docker
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Applications-Docker5ul0fl6184j0
|
||
:END:
|
||
Docker is an awesome tool for reproducible development environments.
|
||
Due to this, I absolutely need a mode for editing Dockerfiles.
|
||
#+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
|
||
|
||
The =docker= package also provides interactivity with Docker and
|
||
docker-compose from Emacs.
|
||
#+begin_src emacs-lisp
|
||
(use-package docker
|
||
:defer t
|
||
:straight (:build t))
|
||
#+end_src
|
||
|
||
*** Elfeed
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Applications-Elfeedoip0fl6184j0
|
||
:END:
|
||
Elfeed is a nice Atom and RSS reader for Emacs. The only thing I want
|
||
to change for now is the default search filter: I want to see not only
|
||
unread news but read news as well, a bit like my emails; and where the
|
||
database is to be stored.
|
||
#+begin_src emacs-lisp
|
||
(use-package elfeed
|
||
:defer t
|
||
:straight (:build t)
|
||
:custom
|
||
((elfeed-search-filter "@6-months-ago")
|
||
(elfeed-db-directory (expand-file-name ".elfeed-db"
|
||
user-emacs-directory))))
|
||
#+end_src
|
||
|
||
Elfeed-goodies is a package which enhances the Elfeed experience.
|
||
Aside from running its setup command as soon as possible, I also set
|
||
in this code block all my keybinds for Elfeed here.
|
||
#+begin_src emacs-lisp
|
||
(use-package elfeed-goodies
|
||
:defer t
|
||
:after elfeed
|
||
:commands elfeed-goodies/setup
|
||
:straight (:build t)
|
||
:init
|
||
(elfeed-goodies/setup)
|
||
:general
|
||
(phundrak/undefine
|
||
:keymaps '(elfeed-show-mode-map elfeed-search-mode-map)
|
||
:packages 'elfeed
|
||
"DEL" nil
|
||
"s" nil)
|
||
(phundrak/evil
|
||
:keymaps 'elfeed-show-mode-map
|
||
:packages 'elfeed
|
||
"+" #'elfeed-show-tag
|
||
"-" #'elfeed-show-untag
|
||
"«" #'elfeed-show-prev
|
||
"»" #'elfeed-show-next
|
||
"b" #'elfeed-show-visit
|
||
"C" #'elfeed-kill-link-url-at-point
|
||
"d" #'elfeed-show-save-enclosure
|
||
"l" #'elfeed-show-next-link
|
||
"o" #'elfeed-goodies/show-ace-link
|
||
"q" #'elfeed-kill-buffer
|
||
"S" #'elfeed-show-new-live-search
|
||
"u" #'elfeed-show-tag--unread
|
||
"y" #'elfeed-show-yank)
|
||
(phundrak/evil
|
||
:keymaps 'elfeed-search-mode-map
|
||
:packages 'elfeed
|
||
"«" #'elfeed-search-first-entry
|
||
"»" #'elfeed-search-last-entry
|
||
"b" #'elfeed-search-browse-url
|
||
"f" '(nil :which-key "filter")
|
||
"fc" #'elfeed-search-clear-filter
|
||
"fl" #'elfeed-search-live-filter
|
||
"fs" #'elfeed-search-set-filter
|
||
"u" '(nil :which-key "update")
|
||
"us" #'elfeed-search-fetch
|
||
"uS" #'elfeed-search-update
|
||
"uu" #'elfeed-update
|
||
"uU" #'elfeed-search-update--force
|
||
"y" #'elfeed-search-yank)
|
||
(phundrak/major-leader-key
|
||
:keymaps 'elfeed-search-mode-map
|
||
:packages 'elfeed
|
||
"c" #'elfeed-db-compact
|
||
"t" '(nil :which-key "tag")
|
||
"tt" #'elfeed-search-tag-all-unread
|
||
"tu" #'elfeed-search-untag-all-unread
|
||
"tT" #'elfeed-search-tag-all
|
||
"tU" #'elfeed-search-untag-all))
|
||
#+end_src
|
||
|
||
Last but not least, my Elfeed configuration is stored in an org file
|
||
thanks to ~elfeed-org~.
|
||
#+begin_src emacs-lisp
|
||
(use-package elfeed-org
|
||
:defer t
|
||
:after elfeed
|
||
:straight (:build t)
|
||
:init
|
||
(elfeed-org)
|
||
:config
|
||
(setq rmh-elfeed-org-files '("~/org/elfeed.org")))
|
||
#+end_src
|
||
|
||
*** 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.
|
||
#+begin_src emacs-lisp
|
||
(use-package mu4e
|
||
:after all-the-icons
|
||
:straight (:build t :location site)
|
||
:commands mu4e mu4e-compose-new
|
||
:init
|
||
(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)))
|
||
(defmacro mu4e-view-mode--prepare ()
|
||
`(lambda () (visual-line-mode 1)))
|
||
:gfhook ('mu4e-view-mode-hook (mu4e-view-mode--prepare))
|
||
:general
|
||
<<mu4e-keybindings-undef>>
|
||
<<mu4e-keybindings-view>>
|
||
<<mu4e-keybindings-view-no-prefix>>
|
||
<<mu4e-keybindings-header>>
|
||
<<mu4e-keybindings-header-no-leader>>
|
||
<<mu4e-keybindings-message>>
|
||
|
||
:config
|
||
<<mu4e-mail-service>>
|
||
<<mu4e-mail-on-machine>>
|
||
<<mu4e-no-signature>>
|
||
<<mu4e-bookmarks>>
|
||
|
||
(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)
|
||
|
||
<<mu4e-fancy-marks>>
|
||
<<mu4e-vertical-split>>
|
||
<<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
|
||
|
||
Quick sidenote: on ArchLinux, you’ll need to install either ~mu~ or
|
||
~mu-git~ from the AUR in order to use mu4e. I also have a ~.desktop~ file
|
||
so I can open mu4e directly from my program picker. It uses the shell
|
||
script ~emacsmail~ I’ve written [[file:bin.org::#Emacsmail-afffb7cd][here]].
|
||
#+begin_src conf-desktop :tangle ~/.local/share/applications/mu4e.desktop
|
||
[Desktop Entry]
|
||
Name=Mu4e
|
||
GenericName=Mu4e
|
||
Comment=Maildir Utils for Emacs
|
||
MimeType=x-scheme-handler/mailto;
|
||
Exec=/home/phundrak/.local/bin/emacsmail %U
|
||
Icon=emacs
|
||
Type=Application
|
||
Terminal=false
|
||
Categories=Network;Email;TextEditor
|
||
StartupWMClass=Gnus
|
||
Keywords=Text;Editor;
|
||
#+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-root-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
|
||
|
||
***** 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-from-copy-to-gen
|
||
#+begin_src emacs-lisp :tangle no :exports none :var regex="test"
|
||
(mapconcat (lambda (x) (concat x ":" regex))
|
||
'("f" "c" "t")
|
||
" OR ")
|
||
#+end_src
|
||
|
||
#+name: mu4e-bookmarks-filter-uni
|
||
#+headers: :tangle no :cache yes
|
||
#+begin_src emacs-lisp
|
||
(let ((regex "/.*up8\\.edu|.*univ-paris8.*/"))
|
||
<<mu4e-bookmarks-from-copy-to-gen>>)
|
||
#+end_src
|
||
|
||
#+RESULTS[f1b50a07521c59a4ccd72f6f8ec11431ac618139]: mu4e-bookmarks-filter-uni
|
||
: f:/.*up8\.edu|.*univ-paris8.*/ OR c:/.*up8\.edu|.*univ-paris8.*/ OR t:/.*up8\.edu|.*univ-paris8.*/
|
||
|
||
Next I need an inbox dedicated to the association I’m part of.
|
||
#+name: mu4e-bookmarks-filter-asso
|
||
#+headers: :tangle no :cache yes
|
||
#+begin_src emacs-lisp
|
||
(let ((regex "/.*supran\\.fr/"))
|
||
<<mu4e-bookmarks-from-copy-to-gen>>)
|
||
#+end_src
|
||
|
||
#+RESULTS[981592042b62118e3bb82ce802bdfe9647a200c7]: mu4e-bookmarks-filter-asso
|
||
: f:/.*supran\.fr/ OR c:/.*supran\.fr/ OR t:/.*supran\.fr/
|
||
|
||
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" "t" "f")
|
||
" OR "))
|
||
'("ateliers-emacs.framalistes.org" "ateliers-paris.emacs-doctor.com")
|
||
" OR ")
|
||
#+end_src
|
||
|
||
#+RESULTS[7f37e2a9e37056a22e7e168ad0bef1189b1210c7]: mu4e-bookmarks-filter-emacs-list
|
||
: list:ateliers-emacs.framalistes.org OR t:ateliers-emacs.framalistes.org OR f:ateliers-emacs.framalistes.org OR list:ateliers-paris.emacs-doctor.com OR t:ateliers-paris.emacs-doctor.com OR f:ateliers-paris.emacs-doctor.com
|
||
|
||
Another bookmark I wish to have is one dedicated to emails related to
|
||
issues and PRs from Github.
|
||
#+name: mu4e-bookmarks-filter-github-list
|
||
#+headers: :tangle no :cache yes
|
||
#+begin_src emacs-lisp
|
||
(string-join '("list:/.*\\.github\\.com/"
|
||
"to:/.*noreply\\.github\\.com/")
|
||
" OR ")
|
||
#+end_src
|
||
|
||
#+RESULTS[e070eb4d48660380191a28686d8002378dfc5a9a]: mu4e-bookmarks-filter-github-list
|
||
: list:/.*\.github\.com/ OR to:/.*noreply\.github\.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))
|
||
'("f" "t" "list")
|
||
" OR "))
|
||
'("CONLANG@LISTSERV.BROWN.EDU" "AUXLANG@LISTSERV.BROWN.EDU")
|
||
" OR ")
|
||
#+end_src
|
||
|
||
#+RESULTS[8b8e964b9777bc1d98542218d79a3537283ac03c]: mu4e-bookmarks-filter-conlang-list
|
||
: f:CONLANG@LISTSERV.BROWN.EDU OR t:CONLANG@LISTSERV.BROWN.EDU OR list:CONLANG@LISTSERV.BROWN.EDU OR f:AUXLANG@LISTSERV.BROWN.EDU OR t: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-asso()>>")
|
||
,(format "(%s)"
|
||
<<mu4e-bookmarks-filter-emacs-list>>)
|
||
,(format "(%s)"
|
||
<<mu4e-bookmarks-filter-uni>>)))
|
||
" AND NOT ")
|
||
#+end_src
|
||
|
||
#+RESULTS[f9397c5b9d6f7e371770c2a527536721f9bcc621]: mu4e-bookmarks-inbox-filters
|
||
: NOT flag:trashed AND (maildir:/Inbox OR maildir:/Junk) AND NOT (f:CONLANG@LISTSERV.BROWN.EDU OR t:CONLANG@LISTSERV.BROWN.EDU OR list:CONLANG@LISTSERV.BROWN.EDU OR f:AUXLANG@LISTSERV.BROWN.EDU OR t:AUXLANG@LISTSERV.BROWN.EDU OR list:AUXLANG@LISTSERV.BROWN.EDU) AND NOT (f:/.*supran.fr/ OR c:/.*supran.fr/ OR t:/.*supran.fr/) AND NOT (list:ateliers-emacs.framalistes.org OR t:ateliers-emacs.framalistes.org OR f:ateliers-emacs.framalistes.org OR list:ateliers-paris.emacs-doctor.com OR t:ateliers-paris.emacs-doctor.com OR f: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 "University"
|
||
:key ?u
|
||
:query ,(format "%s AND %s"
|
||
"<<mu4e-bookmarks-default-filter()>>"
|
||
"<<mu4e-bookmarks-filter-uni()>>"))
|
||
(:name "Supran"
|
||
:key ?S
|
||
:query ,(format "%s AND %s"
|
||
"<<mu4e-bookmarks-default-filter()>>"
|
||
"<<mu4e-bookmarks-filter-asso()>>"))
|
||
(:name "Emacs"
|
||
:key ?e
|
||
:query ,(format "%s AND %s"
|
||
"<<mu4e-bookmarks-default-filter()>>"
|
||
<<mu4e-bookmarks-filter-emacs-list>>))
|
||
(:name "Github"
|
||
:key ?g
|
||
:query ,(format "%s AND (%s)"
|
||
"<<mu4e-bookmarks-default-filter()>>"
|
||
"<<mu4e-bookmarks-filter-github-list()>>"))
|
||
(:name "Linguistics"
|
||
:key ?l
|
||
:query ,(format "%s AND %s"
|
||
"<<mu4e-bookmarks-default-filter()>>"
|
||
<<mu4e-bookmarks-filter-conlang-list>>))
|
||
(: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[2075e217490452e36b879f71ae39dc777cccee83]: mu4e-bookmarks
|
||
| :name | Inbox | :key | 105 | :query | NOT flag:trashed AND (maildir:/Inbox OR maildir:/Junk) AND NOT (f:CONLANG@LISTSERV.BROWN.EDU OR t:CONLANG@LISTSERV.BROWN.EDU OR list:CONLANG@LISTSERV.BROWN.EDU OR f:AUXLANG@LISTSERV.BROWN.EDU OR t:AUXLANG@LISTSERV.BROWN.EDU OR list:AUXLANG@LISTSERV.BROWN.EDU) AND NOT (f:/.*supran\.fr/ OR c:/.*supran\.fr/ OR t:/.*supran\.fr/) AND NOT (list:ateliers-emacs.framalistes.org OR t:ateliers-emacs.framalistes.org OR f:ateliers-emacs.framalistes.org OR list:ateliers-paris.emacs-doctor.com OR t:ateliers-paris.emacs-doctor.com OR f: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 | University | :key | 117 | :query | NOT flag:trashed AND (maildir:/Inbox OR maildir:/Junk) AND f:/.*up8\.edu | .*univ-paris8.*/ OR c:/.*up8\.edu | .*univ-paris8.*/ OR t:/.*up8\.edu | .*univ-paris8.*/ |
|
||
| :name | Supran | :key | 83 | :query | NOT flag:trashed AND (maildir:/Inbox OR maildir:/Junk) AND f:/.*supran\.fr/ OR c:/.*supran\.fr/ OR t:/.*supran\.fr/ | | | |
|
||
| :name | Emacs | :key | 101 | :query | NOT flag:trashed AND (maildir:/Inbox OR maildir:/Junk) AND list:ateliers-emacs.framalistes.org OR t:ateliers-emacs.framalistes.org OR f:ateliers-emacs.framalistes.org OR list:ateliers-paris.emacs-doctor.com OR t:ateliers-paris.emacs-doctor.com OR f:ateliers-paris.emacs-doctor.com | | | |
|
||
| :name | Github | :key | 103 | :query | NOT flag:trashed AND (maildir:/Inbox OR maildir:/Junk) AND list:/.*\.github.com/ | | | |
|
||
| :name | Linguistics | :key | 108 | :query | NOT flag:trashed AND (maildir:/Inbox OR maildir:/Junk) AND f:CONLANG@LISTSERV.BROWN.EDU OR t:CONLANG@LISTSERV.BROWN.EDU OR list:CONLANG@LISTSERV.BROWN.EDU OR f:AUXLANG@LISTSERV.BROWN.EDU OR t:AUXLANG@LISTSERV.BROWN.EDU OR list:AUXLANG@LISTSERV.BROWN.EDU | | | |
|
||
| :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 | | | |
|
||
|
||
***** Dealing with spammers
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Applications-Email-Mu4e-Dealing-with-spammers-tid4mw51l7j0
|
||
:END:
|
||
I’m sure you have received at least one email recently from a sketchy
|
||
email address asking you something that might be completely unrelated
|
||
to what you do, or at least somewhat related. Fortunately, [[https://twitter.com/Boris/status/1360208504544444417][we have a
|
||
hero]]! Now, let me write a function that will insert their pre-written
|
||
text at point so I don’t have to go back to their Twitter thread each
|
||
time I want to shut spammers up.
|
||
#+begin_src emacs-lisp
|
||
(defun reply-to-bill ()
|
||
(interactive)
|
||
(insert "Please forward this email to bill@noprocurement.com,
|
||
and delete my email, as I’ll be changing jobs soon, and this
|
||
email address will no longer be active.
|
||
|
||
Bill Whiskoney is a senior partner at Nordic Procurement
|
||
Services, and he handles our budget and will help you further or
|
||
introduce you to someone who can."))
|
||
#+end_src
|
||
|
||
If you want the full story, make sure to read the whole thread, I
|
||
guarantee it, it’s worth your time! And in case the Twitter thread
|
||
disappear in the future, [[https://threader.app/thread/1360208504544444417][here is a backup]].
|
||
|
||
***** 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
|
||
|
||
#+name: mu4e-vertical-split
|
||
#+begin_src emacs-lisp :tangle no
|
||
(defun my/set-mu4e-headers-width ()
|
||
(let ((width (window-body-width))
|
||
(threshold (+ 120 80)))
|
||
(setq mu4e-split-view (if (> width threshold)
|
||
'vertical
|
||
'horizontal))
|
||
(message "Window width: %d\tthreshold: %d\nSplit: %S"
|
||
width
|
||
threshold
|
||
mu4e-split-view)))
|
||
|
||
(setq mu4e-headers-visible-columns 120
|
||
mu4e-headers-visible-lines 15)
|
||
(add-hook 'mu4e-headers-mode-hook #'my/set-mu4e-headers-width)
|
||
#+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
|
||
(phundrak/undefine
|
||
:keymaps 'mu4e-view-mode-map
|
||
:packages 'mu4e
|
||
"S" nil
|
||
"r" nil
|
||
"c" nil
|
||
"gu" nil)
|
||
|
||
(phundrak/undefine
|
||
:keymaps '(mu4e-view-mode-map mu4e-headers-mode-map)
|
||
:packages 'mu4e
|
||
"s" nil)
|
||
#+end_src
|
||
|
||
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 | mu4e-view-mark-thread | mark 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 | |
|
||
|
||
#+name: mu4e-keybindings-view
|
||
#+begin_src emacs-lisp :tangle no
|
||
(phundrak/major-leader-key
|
||
:keymaps 'mu4e-view-mode-map
|
||
:packages 'mu4e
|
||
<<general-keybindings-gen(table=mu4e-keybindings-view-tbl)>>)
|
||
#+end_src
|
||
|
||
Two other keybinds are added without a prefix, just for the sake of
|
||
convenience.
|
||
#+name: mu4e-keybindings-view-no-prefix
|
||
#+begin_src emacs-lisp
|
||
(phundrak/evil
|
||
:keymaps 'mu4e-view-mode-map
|
||
:packages 'mu4e
|
||
"«" #'mu4e-view-headers-prev
|
||
"»" #'mu4e-view-headers-next)
|
||
#+end_src
|
||
|
||
I’ll also declare two keybinds for mu4e’s headers mode.
|
||
#+name: mu4e-keybindings-header
|
||
#+begin_src emacs-lisp :tangle no
|
||
(phundrak/major-leader-key
|
||
:keymaps 'mu4e-headers-mode-map
|
||
:packages 'mu4e
|
||
"t" '(mu4e-view-mark-thread :which-key "mark thread")
|
||
"s" 'swiper)
|
||
#+end_src
|
||
|
||
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
|
||
(phundrak/evil
|
||
:keymaps 'mu4e-headers-mode-map
|
||
:packages 'mu4e
|
||
"c" #'evil-backward-char
|
||
"t" #'evil-next-line
|
||
"s" #'evil-previous-line
|
||
"r" #'evil-forward-char)
|
||
#+end_src
|
||
|
||
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
|
||
(phundrak/major-leader-key
|
||
:keymaps 'message-mode-map
|
||
:packages 'mu4e
|
||
<<general-keybindings-gen(table=mu4e-keybindings-message-tbl)>>)
|
||
#+end_src
|
||
|
||
**** 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))
|
||
:custom-face
|
||
(mu4e-replied-face ((t (:weight normal :foreground "#b48ead"))))
|
||
:config
|
||
(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)
|
||
(add-hook 'mu4e-headers-mode (lambda () (toggle-truncate-lines -1)))
|
||
(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"
|
||
(string-trim
|
||
(replace-regexp-in-string
|
||
(regexp-quote "\n")
|
||
"\n\n"
|
||
(with-temp-buffer
|
||
(insert-file-contents mail-signature-file)
|
||
(buffer-string))))))
|
||
:general
|
||
(phundrak/major-leader-key
|
||
:keymaps 'org-msg-edit-mode-map
|
||
:packages 'org-msg
|
||
"," #'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))
|
||
#+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 "flag:unread"))
|
||
#+end_src
|
||
|
||
*** EMMS and Media
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Applications-EMMS-ij71fr61v8j0
|
||
:END:
|
||
EMMS, also known as the /Emacs MultiMedia System/, allows the user to
|
||
interact through Emacs with multimedia elements such as music and
|
||
videos. My main use for it will be for music with MPD (see its
|
||
configuration [[file:mpd.org][here]]).
|
||
#+begin_src emacs-lisp
|
||
(use-package emms
|
||
:defer t
|
||
:after all-the-icons
|
||
:straight (:build t)
|
||
:init
|
||
(require 'emms-setup)
|
||
(emms-all)
|
||
(add-to-list 'emms-info-functions 'emms-info-mpd)
|
||
(add-to-list 'emms-player-list 'emms-player-mpd)
|
||
(emms-player-mpd-sync-from-mpd)
|
||
(emms-player-mpd-connect)
|
||
<<emms-media-hydra>>
|
||
(defun emms-player-toggle-pause ()
|
||
(interactive)
|
||
(shell-command-and-echo "mpc toggle"))
|
||
:custom
|
||
((emms-source-file-default-directory (expand-file-name "~/Music"))
|
||
(emms-player-mpd-server-name "localhost")
|
||
(emms-player-mpd-server-port "6600")
|
||
(emms-player-mpd-music-directory (expand-file-name "~/Music")))
|
||
:general
|
||
(phundrak/undefine
|
||
:keymaps 'emms-browser-mode-map
|
||
:packages 'emms
|
||
"s" nil
|
||
"r" nil)
|
||
(phundrak/evil
|
||
:keymaps 'emms-browser-mode-map
|
||
:packages 'emms
|
||
"a" #'emms-browser-add-tracks
|
||
"A" #'emms-browser-add-tracks-and-play
|
||
"b" '(nil :which-key "browse by")
|
||
"bA" #'emms-browse-by-album
|
||
"ba" #'emms-browse-by-artist
|
||
"bg" #'emms-browse-by-genre
|
||
"bs" #'emms-smart-browse
|
||
"by" #'emms-browse-by-year
|
||
"c" #'emms-browser-clear-playlist
|
||
"S" '(nil :which-key "search")
|
||
"SA" '(emms-browser-search-by-album :which-key "by album")
|
||
"Sa" '(emms-browser-search-by-artist :which-key "by artist")
|
||
"Ss" '(emms-browser-search-by-names :which-key "by name")
|
||
"St" '(emms-browser-search-by-names :which-key "by title")
|
||
"q" #'kill-this-buffer)
|
||
(phundrak/evil
|
||
:keymaps 'emms-playlist-mode-map
|
||
:packages 'emms
|
||
"d" #'emms-playlist-mode-kill-track
|
||
"p" #'emms-playlist-mode-play-smart
|
||
"q" #'kill-this-buffer)
|
||
(phundrak/leader-key
|
||
:infix "m"
|
||
:packages 'emms
|
||
"" '(nil :which-key "media")
|
||
"." #'hydra-media/body
|
||
"«" #'emms-player-mpd-previous
|
||
"»" #'emms-player-mpd-next
|
||
"c" #'emms-player-mpd-clear
|
||
"e" '(nil :which-key "emms")
|
||
"eb" #'emms-browser
|
||
"ep" #'emms-playlist-mode-go
|
||
"es" #'emms-player-mpd-show
|
||
"p" '((lambda ()
|
||
(interactive)
|
||
(shell-command-and-echo "mpc toggle"))
|
||
:which-key "mpc toggle")
|
||
"u" '(nil :which-key "update")
|
||
"um" #'emms-player-mpd-update-all
|
||
"uc" #'emms-cache-set-from-mpd-all))
|
||
#+end_src
|
||
|
||
I also want to create a small hydra for manipulating MPD:
|
||
#+name: emms-media-hydra
|
||
#+begin_src emacs-lisp :tangle no
|
||
(defun shell-command-and-echo (command &optional echo prefix)
|
||
"Run COMMAND and display the result of ECHO prefixed by PREFIX.
|
||
|
||
Run COMMAND as a shell command.
|
||
|
||
If ECHO is non nil, display the result of its execution as a
|
||
shell command to the minibuffer through `MESSAGE'.
|
||
|
||
If PREFIX is non nil, it will prefix the output of ECHO in the
|
||
minibuffer, both separated by a single space."
|
||
(progn
|
||
(with-temp-buffer
|
||
(shell-command command
|
||
(current-buffer)
|
||
(current-buffer))
|
||
(when echo
|
||
(message "%s%s"
|
||
(if prefix
|
||
(concat prefix " ")
|
||
"")
|
||
(string-trim
|
||
(shell-command-to-string "mpc volume")))))))
|
||
|
||
(defhydra hydra-media ()
|
||
"
|
||
^Tracks^ ^Volume^ ^Other^
|
||
^^^^^^^^----------------------------
|
||
[_c_] %s(all-the-icons-material \"skip_previous\" :height 1.0 :v-adjust -0.2) [_t_] %s(all-the-icons-material \"volume_down\" :height 1.0 :v-adjust -0.2) [_p_] %s(all-the-icons-material \"play_arrow\" :height 1.0 :v-adjust -0.2)
|
||
[_r_] %s(all-the-icons-material \"skip_next\" :height 1.0 :v-adjust -0.2) [_s_] %s(all-the-icons-material \"volume_up\" :height 1.0 :v-adjust -0.2) [_S_] %s(all-the-icons-material \"stop\" :height 1.0 :v-adjust -0.2)
|
||
|
||
[_q_] quit
|
||
"
|
||
("c" emms-player-mpd-previous)
|
||
("r" emms-player-mpd-next)
|
||
("t" (shell-command-and-echo "mpc volume -2" "mpc volume" "mpc"))
|
||
("s" (shell-command-and-echo "mpc volume +2" "mpc volume" "mpc"))
|
||
("p" (shell-command-and-echo "mpc toggle"))
|
||
("S" emms-player-mpd-stop)
|
||
("q" nil :exit t))
|
||
#+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
|
||
(phundrak/evil
|
||
:keymaps 'nov-mode-map
|
||
:packages 'nov
|
||
"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:
|
||
~pdf-tools~ enables PDF support for Emacs, much better than its built-in
|
||
support with DocView. Aside from the classical settings such as
|
||
keybinds, I also enable the midnight colors by default; think of it as
|
||
an equivalent of Zathura’s recolor feature which kind of enables a
|
||
dark mode for PDFs.
|
||
#+begin_src emacs-lisp
|
||
(use-package pdf-tools
|
||
:defer t
|
||
:magic ("%PDF" . pdf-view-mode)
|
||
:straight (:build t)
|
||
:mode (("\\.pdf\\'" . pdf-view-mode))
|
||
:hook (pdf-tools-enabled . pdf-view-midnight-minor-mode)
|
||
:general
|
||
(phundrak/evil
|
||
:keymaps 'pdf-view-mode-map
|
||
:packages 'pdf-tools
|
||
"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)
|
||
(phundrak/major-leader-key
|
||
:keymaps 'pdf-view-mode-map
|
||
:packages 'pdf-tools
|
||
"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)
|
||
:config
|
||
(with-eval-after-load 'pdf-view
|
||
(setq pdf-view-midnight-colors '("#d8dee9" . "#2e3440"))))
|
||
#+end_src
|
||
|
||
One thing ~pdf-tools~ doesn’t handle is restoring the PDF to the last
|
||
point it was visited --- in other words, open the PDF where I last
|
||
left it.
|
||
#+begin_src emacs-lisp
|
||
(use-package pdf-view-restore
|
||
:after pdf-tools
|
||
:defer t
|
||
:straight (:build t)
|
||
:hook (pdf-view-mode . pdf-view-restore-mode)
|
||
:config
|
||
(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)
|
||
:config
|
||
(setq magit-clone-default-directory "~/fromGIT/")
|
||
:general
|
||
(:keymaps '(git-rebase-mode-map)
|
||
:packages 'magit
|
||
"C-t" #'evil-next-line
|
||
"C-s" #'evil-previous-line)
|
||
(phundrak/major-leader-key
|
||
:keymaps 'git-rebase-mode-map
|
||
:packages 'magit
|
||
"," #'with-editor-finish
|
||
"k" #'with-editor-cancel
|
||
"a" #'with-editor-cancel)
|
||
(phundrak/leader-key
|
||
:infix "g"
|
||
:packages 'magit
|
||
"" '(nil :wk "git")
|
||
"b" #'magit-blame
|
||
"c" #'magit-clone
|
||
"d" #'magit-dispatch
|
||
"i" #'magit-init
|
||
"s" #'magit-status
|
||
"y" #'my/yadm
|
||
"S" #'magit-stage-file
|
||
"U" #'magit-unstage-file
|
||
"f" '(nil :wk "file")
|
||
"fd" #'magit-diff
|
||
"fc" #'magit-file-checkout
|
||
"fl" #'magit-file-dispatch
|
||
"fF" #'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
|
||
|
||
Finally, it is also possible to use Gitflow’s framework with Magit
|
||
with ~magit-gitflow~:
|
||
#+begin_src emacs-lisp
|
||
(use-package magit-gitflow
|
||
:defer t
|
||
:after magit
|
||
:straight (magit-gitflow :build t
|
||
:type git
|
||
:host github
|
||
:repo "jtatarik/magit-gitflow")
|
||
:hook (magit-mode . turn-on-magit-gitflow))
|
||
#+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 ripgrep
|
||
:straight (:build t)
|
||
:defer t)
|
||
#+end_src
|
||
|
||
#+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
|
||
(setq projectile-switch-project-action #'projectile-dired)
|
||
:config
|
||
(add-to-list 'projectile-ignored-projects "~/"))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(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 () (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
|
||
(phundrak/evil
|
||
:keymaps 'xwidget-webkit-mode-map
|
||
"<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
|
||
"C" #'xwidget-webkit-back
|
||
"R" #'xwidget-webkit-forward
|
||
"C-r" #'xwidget-webkit-reload
|
||
"j" nil
|
||
"k" nil
|
||
"l" nil
|
||
"H" nil
|
||
"L" nil
|
||
"C-d" #'xwidget-webkit-scroll-up
|
||
"C-u" #'xwidget-webkit-scroll-down)
|
||
#+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.
|
||
|
||
** Editing
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Editinggnu1fl6184j0
|
||
:END:
|
||
First, I’ll define some keybindings for easily inserting pairs when
|
||
editing text.
|
||
#+begin_src emacs-lisp
|
||
(general-define-key
|
||
:states 'visual
|
||
"M-[" #'insert-pair
|
||
"M-{" #'insert-pair
|
||
"M-<" #'insert-pair
|
||
"M-'" #'insert-pair
|
||
"M-`" #'insert-pair
|
||
"M-\"" #'insert-pair)
|
||
#+end_src
|
||
|
||
*** Atomic Chrome
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Editing-Atomic-Chrome-2w5bt8y029j0
|
||
:END:
|
||
Why write in your browser when you could write with Emacs? Despite its
|
||
name, this package isn’t only geared towards Chrome/Chromium-based
|
||
browsers but also towards Firefox since its 2.0 version. I find it a
|
||
bit unfortunate Chrome’s name stuck in the package’s name though.
|
||
#+begin_src emacs-lisp
|
||
(use-package atomic-chrome
|
||
:straight (:build t)
|
||
:init
|
||
(atomic-chrome-start-server))
|
||
#+end_src
|
||
|
||
*** 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
|
||
|
||
*** Iedit
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Editing-Iedit-eb98g8q0p8j0
|
||
:END:
|
||
Iedit is a powerful text editing tool that can be used to refactor
|
||
code through the edition of multiple regions at once, be it in a
|
||
region or in a whole buffer.
|
||
#+begin_src emacs-lisp
|
||
(use-package iedit
|
||
:defer t
|
||
:straight (:build t)
|
||
:general
|
||
(phundrak/leader-key
|
||
:infix "r"
|
||
:packages 'iedit
|
||
"" '(nil :which-key "refactor")
|
||
"i" #'iedit-mode))
|
||
#+end_src
|
||
|
||
Since I’m using evil, I’ll also use a compatibility package that adds
|
||
states for iedit.
|
||
#+begin_src emacs-lisp
|
||
(use-package evil-iedit-state
|
||
:after iedit
|
||
:defer t
|
||
:straight (:build t)
|
||
:commands (evil-iedit-state evil-iedit-state/iedit-mode)
|
||
:init
|
||
(setq iedit-curent-symbol-default t
|
||
iedit-only-at-symbol-boundaries t
|
||
iedit-toggle-key-default nil))
|
||
#+end_src
|
||
|
||
*** 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
|
||
:straight (:build 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
|
||
|
||
*** Smartparens
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Editing-Smartparens-zve93mk0k4j0
|
||
:END:
|
||
~smartparens~ is a package similar to ~parinfer~, but while the latter is
|
||
more specialized for Lisp dialects, ~smartparens~ works better with
|
||
other programming languages that still uses parenthesis, but not as
|
||
much as Lisp dialects; think for example C, C++, Rust, Javascript, and
|
||
so on.
|
||
#+begin_src emacs-lisp
|
||
(use-package smartparens
|
||
:defer t
|
||
:straight (:build t)
|
||
:hook (prog-mode . smartparens-mode))
|
||
#+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
|
||
writeroom-mode-line t))
|
||
#+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
|
||
(phundrak/evil
|
||
:keymaps 'dired-mode-map
|
||
:packages 'dired
|
||
"(" #'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
|
||
(phundrak/evil
|
||
:keymaps 'dired-mode-map
|
||
:packages 'dired-x
|
||
"«" #'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
|
||
(phundrak/evil
|
||
:keymaps 'dired-mode-map
|
||
:packages 'dired-du
|
||
"»" #'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)
|
||
:straight (:build t)
|
||
:hook (dired-after-reading . dired-git-info-auto-enable)
|
||
:general
|
||
(phundrak/evil
|
||
:keymaps 'dired-mode-map
|
||
:packages 'dired-git-info
|
||
")" #'dired-git-info-mode)
|
||
: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
|
||
:after (dired all-the-icons)
|
||
:straight (:build t)
|
||
: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
|
||
:after (all-the-icons)
|
||
:straight (:build t)
|
||
:defer t
|
||
:hook (dired-mode . all-the-icons-dired-mode))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package image-dired+
|
||
:after (image-dired)
|
||
:straight (:build t)
|
||
:init (image-diredx-adjust-mode 1))
|
||
#+end_src
|
||
|
||
*** Compilation mode
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Emacs-built-ins-Compilation-mode-7nh817m0t8j0
|
||
:END:
|
||
After reading about a blog article, I found out it is possible to run
|
||
quite a few things through ~compilation-mode~, so why not? First, let’s
|
||
redefine some keybinds for this mode. I’ll also define a general
|
||
keybind in order to re-run my programs from other buffers than the
|
||
~compilation-mode~ buffer. I also want to follow the output of the
|
||
compilation buffer, as well as enable some syntax highlighting.
|
||
#+begin_src emacs-lisp
|
||
(use-package compile
|
||
:defer t
|
||
:straight (compile :type built-in)
|
||
:hook (compilation-mode . colorize-compilation-buffer)
|
||
:init
|
||
(require 'ansi-color)
|
||
(defun colorize-compilation-buffer ()
|
||
(let ((inhibit-read-only t))
|
||
(ansi-color-apply-on-region (point-min) (point-max))))
|
||
:general
|
||
(phundrak/evil
|
||
:keymaps 'compilation-mode-map
|
||
"g" nil
|
||
"r" #'recompile
|
||
"h" nil)
|
||
(phundrak/leader-key
|
||
"R" #'recompile)
|
||
:config
|
||
(setq compilation-scroll-output t))
|
||
|
||
#+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
|
||
(use-package eshell
|
||
:defer t
|
||
:straight (:type built-in :build t)
|
||
:general
|
||
(phundrak/evil
|
||
:keymaps 'eshell-mode-map
|
||
"c" #'evil-backward-char
|
||
"t" #'evil-next-line
|
||
"s" #'evil-previous-line
|
||
"r" #'evil-forward-char)
|
||
(general-define-key
|
||
:keymaps 'eshell-mode-map
|
||
:states 'insert
|
||
"C-a" #'eshell-bol
|
||
"C-e" #'end-of-line)
|
||
:config
|
||
<<eshell-alias-file>>
|
||
<<eshell-concat-shell-command>>
|
||
<<eshell-alias-open>>
|
||
<<eshell-alias-buffers>>
|
||
<<eshell-alias-emacs>>
|
||
<<eshell-alias-mkcd>>)
|
||
#+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.
|
||
#+name: eshell-alias-file
|
||
#+begin_src emacs-lisp :tangle no
|
||
(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:
|
||
#+name: eshell-concat-shell-command
|
||
#+begin_src emacs-lisp :tangle no
|
||
(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.
|
||
#+name: eshell-alias-open
|
||
#+begin_src emacs-lisp :tangle no
|
||
(defalias 'open #'find-file)
|
||
(defalias 'openo #'find-file-other-window)
|
||
#+end_src
|
||
|
||
The default behavior of ~eshell/clear~ is not great at all, although it
|
||
clears the screen it also scrolls all the way down. Therefore, let’s
|
||
alias it to ~eshell/clear-scrollback~ which has the correct behavior.
|
||
#+begin_src emacs-lisp
|
||
(defalias 'eshell/clear #'eshell/clear-scrollback)
|
||
#+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.
|
||
#+name: eshell-alias-buffers
|
||
#+begin_src emacs-lisp :tangle no
|
||
(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.
|
||
#+name: eshell-alias-emacs
|
||
#+begin_src emacs-lisp :tangle no
|
||
(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~.
|
||
#+name: eshell-alias-mkcd
|
||
#+begin_src emacs-lisp :tangle no
|
||
(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
|
||
|
||
**** Autosuggestion
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Emacs-built-ins-Eshell-Autosuggestion-kf6ipm1195j0
|
||
:END:
|
||
I really like fish’s autosuggestion feature, so let’s reproduce it
|
||
here!
|
||
#+begin_src emacs-lisp
|
||
(use-package esh-autosuggest
|
||
:defer t
|
||
:after eshell
|
||
:straight (:build t)
|
||
:hook (eshell-mode . esh-autosuggest-mode)
|
||
:general
|
||
(:keymaps 'esh-autosuggest-active-map
|
||
"C-e" #'company-complete-selection))
|
||
#+end_src
|
||
|
||
**** Commands
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Emacs-built-ins-Eshell-Commands-n8w3fh2195j0
|
||
: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
|
||
|
||
A very useful command I use often in fish is ~z~, a port from bash’s and
|
||
zsh’s command that allows to jump around directories based on how
|
||
often we go in various directories.
|
||
#+begin_src emacs-lisp
|
||
(use-package eshell-z
|
||
:defer t
|
||
:after eshell
|
||
:straight (:build t)
|
||
:hook (eshell-mode . (lambda () (require 'eshell-z))))
|
||
#+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
|
||
:after (eshell)
|
||
: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
|
||
|
||
Another feature I like is fish-like syntax highlight, which brings
|
||
some more colors to Eshell.
|
||
#+begin_src emacs-lisp
|
||
(use-package eshell-syntax-highlighting
|
||
:after (esh-mode eshell)
|
||
:defer t
|
||
:straight (:build t)
|
||
:config
|
||
(eshell-syntax-highlighting-global-mode +1))
|
||
#+end_src
|
||
|
||
Powerline prompts are nice, git-aware prompts are even better!
|
||
~eshell-git-prompt~ is nice, but I prefer to write my own package for
|
||
that.
|
||
#+begin_src emacs-lisp
|
||
(use-package powerline-eshell
|
||
:load-path "~/fromGIT/emacs-packages/powerline-eshell.el/"
|
||
:after eshell)
|
||
#+end_src
|
||
|
||
*** Eww
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Emacs-built-ins-Eww-m1343rs0t8j0
|
||
:END:
|
||
Since Emacs 29, it is possible to automatically rename ~eww~ buffers to
|
||
a more human-readable name, see [[https://protesilaos.com/codelog/2021-10-15-emacs-29-eww-rename-buffers/][Prot’s blog]] post on the matter.
|
||
#+begin_src emacs-lisp
|
||
(use-package eww
|
||
:defer t
|
||
:straight (:type built-in)
|
||
:config
|
||
(setq eww-auto-rename-buffer 'title))
|
||
#+end_src
|
||
|
||
*** Info
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Emacs-built-ins-Info-r7x90j20c5j0
|
||
:END:
|
||
Let’s define some more intuitive keybinds for ~info-mode~.
|
||
#+begin_src emacs-lisp
|
||
(use-package info
|
||
:defer t
|
||
:straight (info :type built-in :build t)
|
||
:general
|
||
(phundrak/evil
|
||
:keymaps 'Info-mode-map
|
||
"c" #'Info-prev
|
||
"t" #'evil-scroll-down
|
||
"s" #'evil-scroll-up
|
||
"r" #'Info-next)
|
||
(phundrak/major-leader-key
|
||
:keymaps 'Info-mode-map
|
||
"?" #'Info-toc
|
||
"b" #'Info-history-back
|
||
"f" #'Info-history-forward
|
||
"m" #'Info-menu
|
||
"t" #'Info-top-node
|
||
"u" #'Info-up))
|
||
#+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)
|
||
(use-package tramp
|
||
:straight (tramp :type built-in :build t)
|
||
:init
|
||
<<tramp-add-yadm>>)
|
||
#+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:
|
||
#+name: tramp-add-yadm
|
||
#+begin_src emacs-lisp :tangle no
|
||
(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
|
||
|
||
** 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
|
||
(phundrak/evil
|
||
:keymaps 'bufler-list-mode-map
|
||
:packages 'bufler
|
||
"?" #'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:
|
||
Since recently, in order to make ~org-cite~ compile properly, we need
|
||
the ~citeproc~ package, a citation processor.
|
||
#+begin_src emacs-lisp
|
||
(use-package citeproc
|
||
:after (org)
|
||
:defer t
|
||
:straight (:build t))
|
||
#+end_src
|
||
|
||
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 t
|
||
:defer t
|
||
:commands (orgtbl-mode)
|
||
:hook ((org-mode . visual-line-mode)
|
||
(org-mode . org-num-mode))
|
||
:custom-face
|
||
(org-macro ((t (:foreground "#b48ead"))))
|
||
:init
|
||
(auto-fill-mode -1)
|
||
:config
|
||
<<org-hydra-babel>>
|
||
(require 'ox-beamer)
|
||
(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-src-preserve-indentation 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-babel-load-languages>>
|
||
<<org-use-sub-superscripts>>
|
||
<<org-latex-compiler>>
|
||
<<org-latex-listings>>
|
||
<<org-latex-default-packages>>
|
||
<<org-export-latex-hyperref-format>>
|
||
<<org-export-latex-minted-options>>
|
||
<<org-latex-pdf-process>>
|
||
<<org-latex-logfiles-add-extensions>>
|
||
<<org-re-reveal>>
|
||
<<org-html-validation>>
|
||
<<org-latex-classes>>
|
||
<<org-publish-projects>>
|
||
:general
|
||
(phundrak/evil
|
||
:keymaps 'org-mode-map
|
||
:packages 'org
|
||
"RET" 'org-open-at-point)
|
||
(phundrak/major-leader-key
|
||
:keymaps 'org-mode-map
|
||
:packages 'org
|
||
<<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)>>)
|
||
|
||
(phundrak/major-leader-key
|
||
:packages 'org
|
||
:keymaps 'org-src-mode-map
|
||
"'" #'org-edit-src-exit
|
||
"k" #'org-edit-src-abort))
|
||
#+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
|
||
: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
|
||
:after (org)
|
||
:defer t
|
||
: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
|
||
|
||
*** 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
|
||
:after org
|
||
:straight (:build t)
|
||
:defer t
|
||
:init
|
||
(add-to-list 'org-babel-load-languages '(latex-as-png . t)))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package ob-restclient
|
||
:straight (:build t)
|
||
:defer t
|
||
:init
|
||
(add-to-list 'org-babel-load-languages '(restclient . t)))
|
||
#+end_src
|
||
|
||
One of the amazing features of org-mode is its literary programming capacities
|
||
by running code blocks from within Org-mode itself. But for that, only a couple
|
||
of languages are supported directly by Org-mode itself, and they need to be
|
||
activated. Here are the languages I activated in my Org-mode configuration:
|
||
#+NAME: org-babel-languages-table
|
||
| C |
|
||
| dot |
|
||
| emacs-lisp |
|
||
| gnuplot |
|
||
| latex |
|
||
| makefile |
|
||
| sass |
|
||
| shell |
|
||
|
||
#+NAME: org-babel-languages-gen
|
||
#+header: :cache yes :results replace
|
||
#+header: :var languages=org-babel-languages-table[,0]
|
||
#+BEGIN_SRC emacs-lisp :exports none :tangle no
|
||
(format "'(%s)"
|
||
(mapconcat (lambda ($language)
|
||
(format "(%s . t)" $language))
|
||
languages
|
||
"\n "))
|
||
#+END_SRC
|
||
|
||
#+RESULTS[6ee465842066eca1ca245b962239bd8a1ef84a10]: org-babel-languages-gen
|
||
: '((C . t)
|
||
: (dot . t)
|
||
: (emacs-lisp . t)
|
||
: (gnuplot . t)
|
||
: (latex . t)
|
||
: (makefile . t)
|
||
: (sass . t)
|
||
: (shell . t))
|
||
|
||
The corresponding code is as follows:
|
||
#+NAME: org-babel-load-languages
|
||
#+BEGIN_SRC emacs-lisp :noweb yes :tangle no
|
||
(org-babel-do-load-languages
|
||
'org-babel-load-languages
|
||
<<org-babel-languages-gen()>>)
|
||
#+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 markdown-mode)
|
||
: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 :tangle no
|
||
(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
|
||
|
||
*** Exporters
|
||
: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
|
||
|
||
**** Epub
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Org-mode-File-export-Epub-w5ycfuz095j0
|
||
:END:
|
||
A 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 (org ox)
|
||
:straight (:build t))
|
||
#+end_src
|
||
|
||
**** HTML
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Org-mode-File-export-HTMLxjc5fl6184j0
|
||
:END:
|
||
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 :tangle no
|
||
(setq org-html-validation-link nil)
|
||
#+END_SRC
|
||
|
||
#+begin_src emacs-lisp
|
||
;; (use-package htmlize
|
||
;; :defer t
|
||
;; :straight (:build t))
|
||
#+end_src
|
||
|
||
This package allows for live-previewing the HTML export of an org
|
||
buffer in an XWidget Webkit browser window. But when testing it, it’s
|
||
not great for large org files, I should keep its usage for smaller org
|
||
files.
|
||
#+begin_src emacs-lisp
|
||
(use-package preview-org-html-mode
|
||
:defer t
|
||
:after (org)
|
||
:straight (preview-org-html-mode :build t
|
||
:type git
|
||
:host github
|
||
:repo "jakebox/preview-org-html-mode")
|
||
:general
|
||
(phundrak/major-leader-key
|
||
:keymaps 'org-mode-map
|
||
:packages 'preview-org-html-mode
|
||
:infix "P"
|
||
"" '(:ignore :which-key "preview")
|
||
"h" #'preview-org-html-mode
|
||
"r" #'preview-org-html-refresh
|
||
"p" #'preview-org-html-pop-window-to-frame)
|
||
:config
|
||
(setq preview-org-html-refresh-configuration 'save))
|
||
#+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 :tangle no
|
||
(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 :tangle no
|
||
(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; namely, I
|
||
need to remove ~inputenc~, ~fontenc~ and ~grffile~. I also added some default
|
||
packages:
|
||
- ~minted~ for syntax highlighting
|
||
- ~cleveref~ for better references to various elements.
|
||
- ~svg~ for inserting SVG files in PDF outputs
|
||
- ~booktabs~ for nicer tables
|
||
- and ~tabularx~ for tabulars with adjustable columns
|
||
#+NAME: org-latex-default-packages
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
(dolist (package '(("AUTO" "inputenc" t ("pdflatex"))
|
||
("T1" "fontenc" t ("pdflatex"))
|
||
("" "grffile" t)))
|
||
(delete package org-latex-default-packages-alist))
|
||
|
||
(dolist (package '(("" "minted")
|
||
("capitalize" "cleveref")
|
||
("" "svg")
|
||
("" "booktabs")
|
||
("" "tabularx")))
|
||
(add-to-list 'org-latex-default-packages-alist package t))
|
||
|
||
(setq org-latex-reference-command "\\cref{%s}")
|
||
#+END_SRC
|
||
|
||
By the way, reference links in LaTeX should be written in this format,
|
||
since we are using ~cleveref~:
|
||
#+NAME: org-export-latex-hyperref-format
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
(setq org-export-latex-hyperref-format "\\ref{%s}")
|
||
#+END_SRC
|
||
|
||
And Minted should be default break lines if a line is too long:
|
||
#+name: org-export-latex-minted-options
|
||
#+begin_src emacs-lisp :tangle no
|
||
(setq org-latex-minted-options '(("breaklines")
|
||
("tabsize" "2")
|
||
("frame" "single")
|
||
("autogobble")))
|
||
#+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 :tangle no
|
||
(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:
|
||
#+name: org-latex-logfiles-add-extensions
|
||
#+begin_src emacs-lisp :tangle no
|
||
(dolist (ext '("bbl" "lot"))
|
||
(add-to-list 'org-latex-logfiles-extensions ext t))
|
||
#+end_src
|
||
|
||
**** Markdown
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Org-mode-File-export-Markdown-g2vh5vz095j0
|
||
:END:
|
||
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 org)
|
||
:straight (:build t))
|
||
#+end_src
|
||
|
||
**** Reveal.js
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Org-mode-File-export-Reveal-js-mzijhel099j0
|
||
:END:
|
||
#+NAME: org-re-reveal
|
||
#+begin_src emacs-lisp
|
||
(use-package org-re-reveal
|
||
:defer t
|
||
:after org
|
||
:straight (:build t)
|
||
:init
|
||
(add-hook 'org-mode-hook (lambda () (require 'org-re-reveal)))
|
||
:config
|
||
(setq org-re-reveal-root "https://cdn.jsdelivr.net/npm/reveal.js"
|
||
org-re-reveal-revealjs-version "4"))
|
||
#+end_src
|
||
|
||
**** SSH Config
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Org-mode-File-export-SSH-Config-tatextz095j0
|
||
:END:
|
||
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 org)
|
||
:straight (:build t))
|
||
#+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 |
|
||
| tc | org-table-move-column-left | |
|
||
| tt | org-table-move-row-down | |
|
||
| ts | org-table-move-row-up | |
|
||
| tr | org-table-move-column-right | |
|
||
| 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 | |
|
||
| tx | org-table-shrink | |
|
||
| tN | org-table-create-with-table.el | |
|
||
| 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
|
||
"/ssh: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 the website 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
|
||
"/ssh: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
|
||
|
||
*** Org-ref and Bibtex configuration
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Org-mode-org-ref-1h586cd085j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
(use-package reftex
|
||
:commands turn-on-reftex
|
||
:init (setq reftex-default-bibliography "~/org/bibliography/references.bib"
|
||
reftex-plug-into-AUCTeX t))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package org-ref
|
||
;; :after (org ox-bibtex pdf-tools)
|
||
:after org
|
||
:defer t
|
||
:straight (:build t)
|
||
:custom-face
|
||
(org-ref-cite-face ((t (:weight bold))))
|
||
:init
|
||
(setq org-ref-completion-library 'org-ref-ivy-cite
|
||
org-latex-logfiles-extensions '("lof" "lot" "aux" "idx" "out" "log" "fbd_latexmk"
|
||
"toc" "nav" "snm" "vrb" "dvi" "blg" "brf" "bflsb"
|
||
"entoc" "ps" "spl" "bbl" "pygtex" "pygstyle"))
|
||
(add-hook 'org-mode-hook (lambda () (require 'org-ref)))
|
||
:config
|
||
(setq bibtex-completion-pdf-field "file"
|
||
bibtex-completion-notes-path "~/org/bibliography/notes/"
|
||
bibtex-completion-bibliography "~/org/bibliography/references.bib"
|
||
bibtex-completion-library-path "~/org/bibliography/bibtex-pdfs/"
|
||
bibtex-completion-pdf-symbol "⌘"
|
||
bibtex-completion-notes-symbol "✎")
|
||
:general
|
||
(phundrak/evil
|
||
:keymaps 'bibtex-mode-map
|
||
:packages 'org-ref
|
||
"C-t" #'org-ref-bibtex-next-entry
|
||
"C-s" #'org-ref-bibtex-previous-entry
|
||
"gt" #'org-ref-bibtex-next-entry
|
||
"gs" #'org-ref-bibtex-previous-entry)
|
||
(phundrak/major-leader-key
|
||
:keymaps '(bibtex-mode-map)
|
||
:packages 'org-ref
|
||
;; Navigation
|
||
"t" #'org-ref-bibtex-next-entry
|
||
"s" #'org-ref-bibtex-previous-entry
|
||
|
||
;; Open
|
||
"b" #'org-ref-open-in-browser
|
||
"n" #'org-ref-open-bibtex-notes
|
||
"p" #'org-ref-open-bibtex-pdf
|
||
|
||
;; Misc
|
||
"h" #'org-ref-bibtex-hydra/body
|
||
"i" #'org-ref-bibtex-hydra/org-ref-bibtex-new-entry/body-and-exit
|
||
"s" #'org-ref-sort-bibtex-entry
|
||
|
||
"l" '(:ignore :which-key "lookup")
|
||
"la" #'arxiv-add-bibtex-entry
|
||
"lA" #'arxiv-get-pdf-add-bibtex-entry
|
||
"ld" #'doi-utils-add-bibtex-entry-from-doi
|
||
"li" #'isbn-to-bibtex
|
||
"lp" #'pubmed-insert-bibtex-from-pmid)
|
||
(phundrak/major-leader-key
|
||
:keymaps 'org-mode-map
|
||
:pakages 'org-ref
|
||
"ic" #'org-ref-insert-link))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package ivy-bibtex
|
||
:defer t
|
||
:straight (:build t)
|
||
:config
|
||
(setq bibtex-completion-pdf-open-function #'find-file)
|
||
:general
|
||
(phundrak/leader-key
|
||
:keymaps '(bibtex-mode-map)
|
||
:packages 'ivy-bibtex
|
||
"m" #'ivy-bibtex))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(after! org
|
||
(require 'org-ref))
|
||
#+end_src
|
||
|
||
*** Org-present
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Org-mode-Org-present-pw04s240w8j0
|
||
:END:
|
||
~org-present~ allows its user to create presentations through ~org-mode~,
|
||
which is really nice! However, most of my configuration will be stolen
|
||
[[https://config.daviwil.com/emacs#org-present][from Daviwil’s]] with minor changes.
|
||
#+begin_src emacs-lisp
|
||
(defun my/org-present-prepare-slide ()
|
||
(org-overview)
|
||
(org-show-entry)
|
||
(org-show-children)
|
||
(org-present-hide-cursor))
|
||
|
||
(defun my/org-present-init ()
|
||
(setq header-line-format " ")
|
||
(org-display-inline-images)
|
||
(my/org-present-prepare-slide))
|
||
|
||
(defun my/org-present-quit ()
|
||
(setq header-line-format nil)
|
||
(org-present-small)
|
||
(org-present-show-cursor))
|
||
|
||
(defun my/org-present-prev ()
|
||
(interactive)
|
||
(org-present-prev)
|
||
(my/org-present-prepare-slide))
|
||
|
||
(defun my/org-present-next ()
|
||
(interactive)
|
||
(org-present-next)
|
||
(my/org-present-prepare-slide))
|
||
|
||
(use-package org-present
|
||
:after org
|
||
:defer t
|
||
:straight (:build t)
|
||
:general
|
||
(phundrak/major-leader-key
|
||
:packages 'org-present
|
||
:keymaps 'org-mode-map
|
||
"p" #'org-present)
|
||
(phundrak/evil
|
||
:states 'normal
|
||
:packages 'org-present
|
||
:keymaps 'org-present-mode-keymap
|
||
"+" #'org-present-big
|
||
"-" #'org-present-small
|
||
"<" #'org-present-beginning
|
||
">" #'org-present-end
|
||
"«" #'org-present-beginning
|
||
"»" #'org-present-end
|
||
"c" #'org-present-hide-cursor
|
||
"C" #'org-present-show-cursor
|
||
"n" #'org-present-next
|
||
"p" #'org-present-prev
|
||
"r" #'org-present-read-only
|
||
"w" #'org-present-read-write
|
||
"q" #'org-present-quit)
|
||
:hook ((org-present-mode . my/org-present-init)
|
||
(org-present-mode-quit . my/org-present-quit)))
|
||
#+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 (:build t)
|
||
:hook
|
||
(org-mode . mixed-pitch-mode)
|
||
(emms-browser-mode . mixed-pitch-mode)
|
||
(emms-playlist-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
|
||
:after (org avy)
|
||
:defer t
|
||
:straight (org-ol-tree :build t
|
||
:host github
|
||
:type git
|
||
:repo "Townk/org-ol-tree")
|
||
:general
|
||
(phundrak/major-leader-key
|
||
:packages 'org-ol-tree
|
||
:keymaps 'org-mode-map
|
||
"O" #'org-ol-tree))
|
||
#+end_src
|
||
|
||
*** Misc
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Org-mode-Misc-l202k9z0l4j0
|
||
:END:
|
||
~org-tree-slide~ is a presentation tool for org-mode.
|
||
#+begin_src emacs-lisp
|
||
(use-package org-tree-slide
|
||
:defer t
|
||
:after org
|
||
:straight (:build t)
|
||
:config
|
||
(setq org-tree-slide-skip-done nil)
|
||
:general
|
||
(phundrak/evil
|
||
:keymaps 'org-mode-map
|
||
:packages 'org-tree-slide
|
||
"<f8>" #'org-tree-slide-mode)
|
||
(phundrak/major-leader-key
|
||
:keymaps 'org-tree-slide-mode-map
|
||
:packages 'org-tree-slide
|
||
"d" (lambda () (interactive (setq org-tree-slide-skip-done (not org-tree-slide-skip-done))))
|
||
"p" #'org-tree-slide-move-next-tree
|
||
"n" #'org-tree-slide-move-previous-tree
|
||
"t" #'org-tree-slide-move-next-tree
|
||
"s" #'org-tree-slide-move-previous-tree
|
||
"u" #'org-tree-slide-content))
|
||
#+end_src
|
||
|
||
** Programming languages
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Programming-languages6et5fl6184j0
|
||
:END:
|
||
*** Tools
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Programming-languages-Tools-w3q5rsg0k4j0
|
||
:END:
|
||
**** 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
|
||
|
||
**** LSP-Mode
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Programming-languages-Tools-LSP-Mode-g4v5rsg0k4j0
|
||
:END:
|
||
[[https://emacs-lsp.github.io/lsp-mode/][~lsp-mode~]] is a mode for Emacs which implements the [[https://github.com/Microsoft/language-server-protocol/][Language Server
|
||
Protocol]] and offers Emacs an IDE-like experience. In short, it’s
|
||
awesome!
|
||
#+begin_src emacs-lisp
|
||
(use-package lsp-mode
|
||
:defer t
|
||
:straight (:build t)
|
||
:init
|
||
(setq lsp-keymap-prefix "C-c l")
|
||
:hook ((rust-mode . lsp-deferred)
|
||
(c-mode . lsp-deferred)
|
||
(c++-mode . lsp-deferred)
|
||
(css-mode . lsp-deferred)
|
||
(dart-mode . lsp-deferred)
|
||
(html-mode . lsp-deferred)
|
||
(lua-mode . lsp-deferred)
|
||
(rust-mode . lsp-deferred)
|
||
(dockerfile-mode . lsp-deferred)
|
||
(lsp-mode . lsp-enable-which-key-integration)
|
||
(lsp-mode . lsp-ui-mode))
|
||
:commands (lsp lsp-deferred)
|
||
:custom
|
||
(lsp-rust-analyzer-cargo-watch-command "clippy")
|
||
(lsp-eldoc-render-all t)
|
||
(lsp-idle-delay 0.6)
|
||
(lsp-rust-analyzer-server-display-inlay-hints t))
|
||
#+end_src
|
||
|
||
I also want all the visual enhancements LSP can provide.
|
||
#+begin_src emacs-lisp
|
||
(use-package lsp-ui
|
||
:after lsp
|
||
:defer t
|
||
:straight (:build t)
|
||
:commands lsp-ui-mode
|
||
:custom
|
||
(lsp-ui-peek-always-show t)
|
||
(lsp-ui-sideline-show-hover t)
|
||
(lsp-ui-doc-enable t)
|
||
:general
|
||
(phundrak/major-leader-key
|
||
:keymaps 'lsp-ui-peek-mode-map
|
||
:packages 'lsp-ui
|
||
"c" #'lsp-ui-pook--select-prev-file
|
||
"t" #'lsp-ui-pook--select-next
|
||
"s" #'lsp-ui-pook--select-prev
|
||
"r" #'lsp-ui-pook--select-next-file))
|
||
#+end_src
|
||
|
||
And let’s enable some intergration with ~ivy~.
|
||
#+begin_src emacs-lisp
|
||
(use-package lsp-ivy
|
||
:straight (:build t)
|
||
:defer t
|
||
:after lsp
|
||
:commands lsp-ivy-workspace-symbol)
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package lsp-treemacs
|
||
:defer t
|
||
:straight (:build t)
|
||
:requires treemacs)
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package exec-path-from-shell
|
||
:defer t
|
||
:straight (:build t)
|
||
:init (exec-path-from-shell-initialize))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package consult-lsp
|
||
:defer t
|
||
:after lsp
|
||
:straight (:build t)
|
||
:general
|
||
(phundrak/evil
|
||
:keymaps 'lsp-mode-map
|
||
[remap xref-find-apropos] #'consult-lsp-symbols))
|
||
#+end_src
|
||
~dap-mode~ is an advanced debugging mode that works through lsp.
|
||
#+begin_src emacs-lisp
|
||
(use-package dap-mode
|
||
:after lsp
|
||
:defer t
|
||
:straight (:build t)
|
||
:config
|
||
(dap-ui-mode)
|
||
(dap-ui-controls-mode 1)
|
||
|
||
(require 'dap-lldb)
|
||
(require 'dap-gdb-lldb)
|
||
|
||
(dap-gdb-lldb-setup)
|
||
(dap-register-debug-template
|
||
"Rust::LLDB Run Configuration"
|
||
(list :type "lldb"
|
||
:request "launch"
|
||
:name "LLDB::Run"
|
||
:gdbpath "rust-lldb"
|
||
:target nil
|
||
:cwd nil)))
|
||
#+end_src
|
||
|
||
*** 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
|
||
|
||
**** CMake
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Programming-languages-DSLs-CMake-vpqkiyp0m5j0
|
||
:END:
|
||
CMake is one of the standard tools for indicating how a project should
|
||
be built. It is not as standard as some other tools such as automake,
|
||
autoconfig, and the likes, but still pretty standard. CMake however
|
||
doesn’t have a major mode available by default, so let’s provide one.
|
||
#+begin_src emacs-lisp
|
||
(use-package cmake-mode
|
||
:defer t
|
||
:straight (:build t))
|
||
#+end_src
|
||
|
||
Let’s enable first some autocompletion for it.
|
||
#+begin_src emacs-lisp
|
||
(use-package company-cmake
|
||
:straight (company-cmake :build t
|
||
:type git
|
||
:host github
|
||
:repo "purcell/company-cmake")
|
||
:after cmake-mode
|
||
:defer t)
|
||
#+end_src
|
||
|
||
And let’s also enable a more advanced CMake fontlock.
|
||
#+begin_src emacs-lisp
|
||
(use-package cmake-font-lock
|
||
:defer t
|
||
:after cmake-mode
|
||
:straight (:build t))
|
||
#+end_src
|
||
|
||
And finally, let’s enable some Eldoc integration for CMake.
|
||
#+begin_src emacs-lisp
|
||
(use-package eldoc-cmake
|
||
:straight (:build t)
|
||
:defer t
|
||
:after cmake-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
|
||
|
||
**** Graphviz
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Programming-languages-DSLs-Graphviz-zifi28c1e8j0
|
||
:END:
|
||
[[https://graphviz.org/][Graphviz]], often known with ~dot~, allows to programatically create
|
||
visual graphs and networks.
|
||
#+begin_src emacs-lisp
|
||
(use-package graphviz-dot-mode
|
||
:straight (:build t)
|
||
:defer t
|
||
:config
|
||
(setq graphviz-dot-indent-width 4))
|
||
#+end_src
|
||
|
||
**** Markdown
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Programming-languages-DSLs-Markdown-7sljsrb1y8j0
|
||
:END:
|
||
Yes, I love org-mode and I largely prefer to use it instead of
|
||
Markdown due to its far superior power and abilities. But still,
|
||
sometimes I need to use Markdown because not everyone use org-mode,
|
||
unfortunately.
|
||
#+begin_src emacs-lisp
|
||
(use-package markdown-mode
|
||
:defer t
|
||
:straight t
|
||
:mode
|
||
(("\\.mkd\\'" . markdown-mode)
|
||
("\\.mdk\\'" . markdown-mode)
|
||
("\\.mdx\\'" . markdown-mode))
|
||
:hook (markdown-mode . orgtbl-mode)
|
||
:general
|
||
(phundrak/evil
|
||
:keymaps 'markdown-mode-map
|
||
:packages 'markdown-mode
|
||
"M-RET" #'markdown-insert-list-item
|
||
"M-c" #'markdown-promote
|
||
"M-t" #'markdown-move-down
|
||
"M-s" #'markdown-move-up
|
||
"M-r" #'markdown-demote)
|
||
(phundrak/major-leader-key
|
||
:keymaps 'markdown-mode-map
|
||
:packages 'markdown-mode
|
||
"{" #'markdown-backward-paragraph
|
||
"}" #'markdown-forward-paragraph
|
||
"]" #'markdown-complete
|
||
">" #'markdown-indent-region
|
||
"»" #'markdown-indent-region
|
||
"<" #'markdown-outdent-region
|
||
"«" #'markdown-outdent-region
|
||
"n" #'markdown-next-link
|
||
"p" #'markdown-previous-link
|
||
"f" #'markdown-follow-thing-at-point
|
||
"k" #'markdown-kill-thing-at-point
|
||
"c" '(nil :which-key "command")
|
||
"c]" #'markdown-complete-buffer
|
||
"cc" #'markdown-check-refs
|
||
"ce" #'markdown-export
|
||
"cm" #'markdown-other-window
|
||
"cn" #'markdown-cleanup-list-numbers
|
||
"co" #'markdown-open
|
||
"cp" #'markdown-preview
|
||
"cv" #'markdown-export-and-preview
|
||
"cw" #'markdown-kill-ring-save
|
||
"h" '(nil :which-key "headings")
|
||
"hi" #'markdown-insert-header-dwim
|
||
"hI" #'markdown-insert-header-setext-dwim
|
||
"h1" #'markdown-insert-header-atx-1
|
||
"h2" #'markdown-insert-header-atx-2
|
||
"h3" #'markdown-insert-header-atx-3
|
||
"h4" #'markdown-insert-header-atx-4
|
||
"h5" #'markdown-insert-header-atx-5
|
||
"h6" #'markdown-insert-header-atx-6
|
||
"h!" #'markdown-insert-header-setext-1
|
||
"h@" #'markdown-insert-header-setext-2
|
||
"i" '(nil :which-key "insert")
|
||
"i-" #'markdown-insert-hr
|
||
"if" #'markdown-insert-footnote
|
||
"ii" #'markdown-insert-image
|
||
"il" #'markdown-insert-link
|
||
"it" #'markdown-insert-table
|
||
"iw" #'markdown-insert-wiki-link
|
||
"l" '(nil :which-key "lists")
|
||
"li" #'markdown-insert-list-item
|
||
"T" '(nil :which-key "toggle")
|
||
"Ti" #'markdown-toggle-inline-images
|
||
"Tu" #'markdown-toggle-url-hiding
|
||
"Tm" #'markdown-toggle-markup-hiding
|
||
"Tt" #'markdown-toggle-gfm-checkbox
|
||
"Tw" #'markdown-toggle-wiki-links
|
||
"t" '(nil :which-key "table")
|
||
"tc" #'markdown-table-move-column-left
|
||
"tt" #'markdown-table-move-row-down
|
||
"ts" #'markdown-table-move-row-up
|
||
"tr" #'markdown-table-move-column-right
|
||
"ts" #'markdown-table-sort-lines
|
||
"tC" #'markdown-table-convert-region
|
||
"tt" #'markdown-table-transpose
|
||
"td" '(nil :which-key "delete")
|
||
"tdc" #'markdown-table-delete-column
|
||
"tdr" #'markdown-table-delete-row
|
||
"ti" '(nil :which-key "insert")
|
||
"tic" #'markdown-table-insert-column
|
||
"tir" #'markdown-table-insert-row
|
||
"x" '(nil :which-key "text")
|
||
"xb" #'markdown-insert-bold
|
||
"xB" #'markdown-insert-gfm-checkbox
|
||
"xc" #'markdown-insert-code
|
||
"xC" #'markdown-insert-gfm-code-block
|
||
"xi" #'markdown-insert-italic
|
||
"xk" #'markdown-insert-kbd
|
||
"xp" #'markdown-insert-pre
|
||
"xP" #'markdown-pre-region
|
||
"xs" #'markdown-insert-strike-through
|
||
"xq" #'markdown-blockquote-region)
|
||
:config
|
||
(setq markdown-fontify-code-blocks-natively t))
|
||
#+end_src
|
||
|
||
Since most of my Mardown files are related to Github, I’d like to be
|
||
able to render Markdown through its API.
|
||
#+begin_src emacs-lisp
|
||
(use-package gh-md
|
||
:defer t
|
||
:after markdown-mode
|
||
:straight (:build t)
|
||
:general
|
||
(phundrak/major-leader-key
|
||
:packages 'gh-md
|
||
:keymaps 'markdown-mode-map
|
||
"cr" #'gh-md-render-buffer))
|
||
#+end_src
|
||
|
||
Tables of content are always nice to have for large files, just like
|
||
with the ~toc-org~ package for org-mode.
|
||
#+begin_src emacs-lisp
|
||
(use-package markdown-toc
|
||
:defer t
|
||
:after markdown-mode
|
||
:straight (:build t)
|
||
:general
|
||
(phundrak/major-leader-key
|
||
:packages 'markdown-toc
|
||
:keymaps 'markdown-mode-map
|
||
"iT" #'markdown-toc-generate-toc))
|
||
#+end_src
|
||
~vmd-mode~ allows the user to live-preview their Github-flavored
|
||
Markdown file quickly. That’s neat!
|
||
#+begin_src emacs-lisp
|
||
(use-package vmd-mode
|
||
:defer t
|
||
:after markdown-mode
|
||
:straight (:build t)
|
||
:custom ((vmd-binary-path (executable-find "vmd")))
|
||
:general
|
||
(phundrak/major-leader-key
|
||
:packages 'vmd-mode
|
||
:keymaps 'markdown-mode-map
|
||
"cP" #'vmd-mode))
|
||
#+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
|
||
|
||
**** PKGBUILD
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Programming-languages-DSLs-PKGBUILD-ll37pjt0m9j0
|
||
:END:
|
||
As I am an ArchLinux user, I sometimes have to interact with PKGBUILD
|
||
files, both from the AUR when I want to install something from there
|
||
or some I write myself.
|
||
#+begin_src emacs-lisp
|
||
(use-package pkgbuild-mode
|
||
:straight (:build t)
|
||
:defer t
|
||
:general
|
||
(phundrak/major-leader-key
|
||
:keymaps 'pkgbuild-mode-map
|
||
"c" #'pkgbuild-syntax-check
|
||
"i" #'pkgbuild-initialize
|
||
"I" #'pkgbuild-increase-release-tag
|
||
"m" #'pkgbuild-makepkg
|
||
"u" '(:ignore :wk "update")
|
||
"us" #'pkgbuild-update-sums-line
|
||
"uS" #'pkgbuild-update-srcinfo))
|
||
#+end_src
|
||
|
||
**** PlantUML
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Programming-languages-DSLs-PlantUML-9zo88og099j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
(use-package plantuml-mode
|
||
:defer t
|
||
:mode ("{{.\\(pum\\|puml\\)\\'" . plantuml-mode)
|
||
:init
|
||
(add-to-list 'org-babel-load-languages '(plantuml . t))
|
||
:general
|
||
(phundrak/major-leader-key
|
||
:keymaps 'plantuml-mode-map
|
||
:packages 'plantuml-mode
|
||
"c" '(nil :which-key "compile")
|
||
"cc" #'plantuml-preview
|
||
"co" #'plantuml-set-output-type)
|
||
:config
|
||
(setq plantuml-default-exec-mode 'jar
|
||
plantuml-jar-path "~/.local/bin/plantuml.jar"
|
||
org-plantuml-jar-path "~/.local/bin/plantuml.jar"))
|
||
#+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
|
||
|
||
**** Toml
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Programming-languages-DSLs-Toml-txu8xvk0k4j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
(use-package toml-mode
|
||
:straight (:build t)
|
||
:defer t
|
||
:mode "/\\(Cargo.lock\\|\\.cargo/config\\)\\'")
|
||
#+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
|
||
|
||
*** General Programming Languages
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Programming-languages-General-Programming-Languageszn56fl6184j0
|
||
:END:
|
||
**** C/C++
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Programming-languages-General-Programming-Languages-C-C-2nfcmoz0i5j0
|
||
:END:
|
||
I know, I know, C and C++ no longer are closely related languages,
|
||
each one of them went their own way and learning C won’t make you a
|
||
good C++ programmer, neither will the other way around. But, They are
|
||
still somewhat related, and Emacs thinks so too.
|
||
#+begin_src emacs-lisp
|
||
(use-package cc-mode
|
||
:straight (:type built-in)
|
||
:defer t
|
||
:init
|
||
(put 'c-c++-backend 'safe-local-variable 'symbolp)
|
||
:config
|
||
(require 'compile)
|
||
:general
|
||
(phundrak/undefine
|
||
:keymaps '(c-mode-map c++-mode-map)
|
||
";" nil)
|
||
(phundrak/major-leader-key
|
||
:keymaps '(c-mode-map c++-mode-map)
|
||
"l" '(:keymap lsp-command-map :which-key "lsp" :package lsp-mode))
|
||
(phundrak/evil
|
||
:keymaps '(c-mode-map c++-mode-map)
|
||
"ga" #'projectile-find-other-file
|
||
"gA" #'projectile-find-other-file-other-window))
|
||
#+end_src
|
||
|
||
Something that is also important when working with these languages is
|
||
respecting the ~.clang-format~ file that may be provided by a project.
|
||
#+begin_src emacs-lisp
|
||
(use-package clang-format+
|
||
:straight (:build t)
|
||
:defer t
|
||
:init
|
||
(add-hook 'c-mode-common-hook #'clang-format+-mode))
|
||
#+end_src
|
||
|
||
However, Emacs’ notion of C++ is somewhat outdated, so we need to
|
||
update its fontlock.
|
||
#+begin_src emacs-lisp
|
||
(use-package modern-cpp-font-lock
|
||
:straight (:build t)
|
||
:defer t
|
||
:hook (c++-mode . modern-c++-font-lock-mode))
|
||
#+end_src
|
||
|
||
**** CommonLisp
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Programming-languages-General-Programming-Languages-CommonLisp-gc2a7s31q5j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
(add-hook 'lisp-mode-hook #'parinfer-rust-mode)
|
||
(add-hook 'lisp-mode-hook (lambda () (smartparens-mode -1)))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package stumpwm-mode
|
||
:straight (:build t)
|
||
:defer t
|
||
:config
|
||
(phundrak/major-leader-key
|
||
:keymaps 'stumpwm-mode-map
|
||
:packages 'stumpwm-mode
|
||
"e" '(:ignore :which-key "eval")
|
||
"ee" #'stumpwm-eval-last-sexp
|
||
"ed" #'stumpwm-eval-defun
|
||
"er" #'stumpwm-eval-region))
|
||
#+end_src
|
||
|
||
**** Dart
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Programming-languages-General-Programming-Languages-Dart-xkr3z8j0m6j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
(use-package dart-mode
|
||
:straight (:build t)
|
||
:defer t
|
||
:mode "\\.dart\\'")
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package lsp-dart
|
||
:straight (:build t)
|
||
:defer t)
|
||
#+end_src
|
||
|
||
**** 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
|
||
|
||
#+begin_src emacs-lisp
|
||
(add-hook 'emacs-lisp-mode-hook (lambda () (smartparens-mode -1)))
|
||
#+end_src
|
||
|
||
Let’s also declare some Elisp-dedicated keybindings, prefixed by a
|
||
comma.
|
||
#+begin_src emacs-lisp
|
||
(phundrak/major-leader-key
|
||
:keymaps 'emacs-lisp-mode-map
|
||
"'" #'ielm
|
||
"c" '(emacs-lisp-byte-compile :which-key "Byte compile")
|
||
"C" '(:ignore :which-key "checkdoc")
|
||
"Cc" #'checkdoc
|
||
"Cs" #'checkdoc-start
|
||
"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
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package package-lint
|
||
:defer t
|
||
:straight (:build t)
|
||
:general
|
||
(phundrak/major-leader-key
|
||
:keymaps 'emacs-lisp-mode-map
|
||
:packages 'package-lint
|
||
"l" #'package-lint-current-buffer))
|
||
#+end_src
|
||
|
||
**** Lua
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Programming-languages-General-Programming-Languages-Lua-p2odc7p0t4j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
;; (use-package lua-mode
|
||
;; :defer t
|
||
;; :straight (:build t)
|
||
;; :mode "\\.lua$"
|
||
;; :interpreter "lua"
|
||
;; :init
|
||
;; (setq lua-indent-level 2
|
||
;; lua-indent-string-contents t)
|
||
;; :general
|
||
;; (phundrak/major-leader-key
|
||
;; :keymaps lua-mode-map
|
||
;; "'" #'lua-show-process-buffer
|
||
;; "h" '(:ignore :wk "help")
|
||
;; "hd" #'lua-search-documentation
|
||
;; "s" '(:ignore :wk "REPL")
|
||
;; "sb" #'lua-send-buffer
|
||
;; "sf" #'lua-send-defun
|
||
;; "sl" #'lua-send-current-line
|
||
;; "sr" #'lua-send-region))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
;; (use-package lsp-lua-emmy
|
||
;; :defer t
|
||
;; :after (lua-mode lsp-mode)
|
||
;; :straight (lsp-lua-emmy :build t
|
||
;; :type git
|
||
;; :host github
|
||
;; :repo "EmmyLua/EmmyLua-LanguageServer")
|
||
;; :hook (lua-mode . lsp-deferred)
|
||
;; :config
|
||
;; (setq lsp-lua-emmy-jar-path (expand-file-name "EmmyLua-LS-all.jar" user-emacs-directory)))
|
||
#+end_src
|
||
|
||
**** Python
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Programming-languages-General-Programming-Languages-Python-7mwd2yq0z6j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
(use-package python
|
||
:defer t
|
||
:straight (:build t)
|
||
:mode (("SConstruct\\'" . python-mode)
|
||
("SConscript\\'" . python-mode)
|
||
("[./]flake8\\'" . conf-mode)
|
||
("/Pipfile\\'" . conf-mode))
|
||
:init
|
||
(setq python-indent-guess-indent-offset-verbose nil)
|
||
(add-hook 'python-mode-local-vars-hook #'lsp)
|
||
(add-to-list 'org-babel-load-languages '(python . t))
|
||
:config
|
||
(setq python-indent-guess-indent-offset-verbose nil)
|
||
(when (and (executable-find "python3")
|
||
(string= python-shell-interpreter "python"))
|
||
(setq python-shell-interpreter "python3")))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(after! org
|
||
(add-to-list 'org-babel-load-languages '(python . t)))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package pytest
|
||
:defer t
|
||
:straight (:build t)
|
||
:commands (pytest-one
|
||
pytest-pdb-one
|
||
pytest-all
|
||
pytest-pdb-all
|
||
pytest-last-failed
|
||
pytest-pdb-last-failed
|
||
pytest-module
|
||
pytest-pdb-module)
|
||
:config
|
||
(add-to-list 'pytest-project-root-files "setup.cfg")
|
||
:general
|
||
(phundrak/major-leader-key
|
||
:keymaps 'python-mode-map
|
||
:infix "t"
|
||
:packages 'pytest
|
||
"" '(:ignore :which-key "test")
|
||
"a" #'python-pytest
|
||
"f" #'python-pytest-file-dwim
|
||
"F" #'python-pytest-file
|
||
"t" #'python-pytest-function-dwim
|
||
"T" #'python-pytest-function
|
||
"r" #'python-pytest-repeat
|
||
"p" #'python-pytest-dispatch))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package poetry
|
||
:defer t
|
||
:straight (:build t)
|
||
:commands (poetry-venv-toggle
|
||
poetry-tracking-mode)
|
||
:config
|
||
(setq poetry-tracking-strategy 'switch-buffer)
|
||
(add-hook 'python-mode-hook #'poetry-tracking-mode))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package pip-requirements
|
||
:defer t
|
||
:straight (:build t))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package pipenv
|
||
:defer t
|
||
:straight (:build t)
|
||
:commands (pipenv-activate
|
||
pipenv-deactivate
|
||
pipenv-shell
|
||
pipenv-open
|
||
pipenv-install
|
||
pipenv-uninstall)
|
||
:hook (python-mode . pipenv-mode)
|
||
:init (setq pipenv-with-projectile nil)
|
||
:general
|
||
(phundrak/major-leader-key
|
||
:keymaps 'python-mode-map
|
||
:packages 'pipenv
|
||
:infix "e"
|
||
"" '(:ignore :which-key "pipenv")
|
||
"a" #'pipenv-activate
|
||
"d" #'pipenv-deactivate
|
||
"i" #'pipenv-install
|
||
"l" #'pipenv-lock
|
||
"o" #'pipenv-open
|
||
"r" #'pipenv-run
|
||
"s" #'pipenv-shell
|
||
"u" #'pipenv-uninstall))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package pyenv
|
||
:defer t
|
||
:straight (:build t)
|
||
:config
|
||
(add-hook 'python-mode-hook #'pyenv-track-virtualenv)
|
||
(add-to-list 'global-mode-string
|
||
'(pyenv-virtual-env-name (" venv:" pyenv-virtual-env-name " "))
|
||
'append))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package pyenv-mode
|
||
:defer t
|
||
:after python
|
||
:straight (:build t)
|
||
:if (executable-find "pyenv")
|
||
:commands (pyenv-mode-versions)
|
||
:general
|
||
(phundrak/major-leader-key
|
||
:packages 'pyenv-mode
|
||
:keymaps 'python-mode-map
|
||
:infix "v"
|
||
"u" #'pyenv-mode-unset
|
||
"s" #'pyenv-mode-set))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package pippel
|
||
:defer t
|
||
:straight (:build t)
|
||
:general
|
||
(phundrak/major-leader-key
|
||
:keymaps 'python-mode-map
|
||
:packages 'pippel
|
||
"P" #'pippel-list-packages))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package pyimport
|
||
:defer t
|
||
:straight (:build t)
|
||
:general
|
||
(phundrak/major-leader-key
|
||
:packages 'pyimport
|
||
:keymaps 'python-mode-map
|
||
:infix "i"
|
||
"" '(:ignore :which-key "imports")
|
||
"i" #'pyimport-insert-missing
|
||
"r" #'pyimport-remove-unused))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package py-isort
|
||
:defer t
|
||
:straight (:build t)
|
||
:general
|
||
(phundrak/major-leader-key
|
||
:keymaps 'python-mode-map
|
||
:packages 'py-isort
|
||
:infix "i"
|
||
"" '(:ignore :which-key "imports")
|
||
"s" #'py-isort-buffer
|
||
"R" #'py-isort-region))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package counsel-pydoc
|
||
:defer t
|
||
:straight (:build t))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package sphinx-doc
|
||
:defer t
|
||
:straight (:build t)
|
||
:init
|
||
(add-hook 'python-mode-hook #'sphinx-doc-mode)
|
||
:general
|
||
(phundrak/major-leader-key
|
||
:keymaps 'python-mode-map
|
||
:packages 'sphinx-doc
|
||
:infix "S"
|
||
"" '(:ignore :which-key "sphinx-doc")
|
||
"e" #'sphinx-doc-mode
|
||
"d" #'sphinx-doc))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package cython-mode
|
||
:defer t
|
||
:straight (:build t)
|
||
:mode "\\.p\\(yx\\|x[di]\\)\\'"
|
||
:config
|
||
(setq cython-default-compile-format "cython -a %s")
|
||
:general
|
||
(phundrak/major-leader-key
|
||
:keymaps 'cython-mode-map
|
||
:packages 'cython-mode
|
||
:infix "c"
|
||
"" '(:ignore :which-key "cython")
|
||
"c" #'cython-compile))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package flycheck-cython
|
||
:defer t
|
||
:straight (:build t)
|
||
:after cython-mode)
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package blacken
|
||
:defer t
|
||
:straight (:build t)
|
||
:init
|
||
(add-hook 'python-mode-hook #'blacken-mode))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package lsp-pyright
|
||
:after lsp-mode
|
||
:defer t
|
||
:straight (:buidl t))
|
||
#+end_src
|
||
|
||
**** Rust
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Programming-languages-General-Programming-Languages-Rust-n3jhh5h0k4j0
|
||
:END:
|
||
Rust is a general programming language, akin to C++ in some ways, but
|
||
much more oriented towards safe code, and much better suited for web
|
||
development. First, let’s install the most important package,
|
||
~rustic~.
|
||
#+begin_src emacs-lisp
|
||
(use-package rustic
|
||
:defer t
|
||
:straight (:build t)
|
||
:mode ("\\.rs$" . rustic-mode)
|
||
:init
|
||
(after! org-src
|
||
(defalias 'org-babel-execute:rust #'org-babel-execute:rustic)
|
||
(add-to-list 'org-src-lang-modes '("rust" . rustic)))
|
||
:general
|
||
(general-define-key
|
||
:keymaps 'rustic-mode-map
|
||
:packages 'lsp
|
||
"M-t" #'lsp-ui-imenu
|
||
"M-?" #'lsp-find-references)
|
||
(phundrak/major-leader-key
|
||
:keymaps 'rustic-mode-map
|
||
:packages 'rustic
|
||
"b" '(:ignore :which-key "build")
|
||
"bb" #'rustic-cargo-build
|
||
"bB" #'rustic-cargo-bench
|
||
"bc" #'rustic-cargo-check
|
||
"bC" #'rustic-cargo-clippy
|
||
"bd" #'rustic-cargo-doc
|
||
"bf" #'rustic-cargo-fmt
|
||
"bn" #'rustic-cargo-new
|
||
"bo" #'rustic-cargo-outdated
|
||
"br" #'rustic-cargo-run
|
||
"l" '(:ignore :which-key "lsp")
|
||
"la" #'lsp-execute-code-action
|
||
"lr" #'lsp-rename
|
||
"lq" #'lsp-workspace-restart
|
||
"lQ" #'lsp-workspace-shutdown
|
||
"ls" #'lsp-rust-analyzer-status
|
||
"t" '(:ignore :which-key "cargo test")
|
||
"ta" #'rustic-cargo-test
|
||
"tt" #'rustic-cargo-current-test)
|
||
:config
|
||
(setq rustic-indent-method-chain t
|
||
rustic-babel-format-src-block nil
|
||
rustic-format-trigger nil)
|
||
(remove-hook 'rustic-mode-hook #'flycheck-mode)
|
||
(remove-hook 'rustic-mode-hook #'flymake-mode-off)
|
||
(remove-hook 'rustic-mode-hook #'rustic-setup-lsp)
|
||
(add-hook 'rustic-mode-local-vars-hook #'rustic-setup-lsp)
|
||
(setq rustic-lsp-client 'lsp-mode))
|
||
#+end_src
|
||
|
||
**** Web programming
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Programming-languages-General-Programming-Languages-Web-programming-7ca40po085j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
(use-package company-web
|
||
:defer t
|
||
:straight (:build t))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package emmet-mode
|
||
:straight (:build t)
|
||
:defer t
|
||
:hook ((css-mode . emmet-mode)
|
||
(html-mode . emmet-mode)
|
||
(web-mode . emmet-mode)
|
||
(sass-mode . emmet-mode)
|
||
(scss-mode . emmet-mode)
|
||
(web-mode . emmet-mode))
|
||
:config
|
||
(general-define-key
|
||
:keymaps 'emmet-mode-keymap
|
||
"C-RET" #'emmet-expand-yas))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package impatient-mode
|
||
:straight (:build t)
|
||
:defer t)
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package slim-mode
|
||
:defer t
|
||
:straight (:build t))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package web-mode
|
||
:defer t
|
||
:straight (:build t)
|
||
:hook ((html-mode . web-mode))
|
||
:mode (("\\.phtml\\'" . web-mode)
|
||
("\\.tpl\\.php\\'" . web-mode)
|
||
("\\.twig\\'" . web-mode)
|
||
("\\.xml\\'" . web-mode)
|
||
("\\.html\\'" . web-mode)
|
||
("\\.htm\\'" . web-mode)
|
||
("\\.[gj]sp\\'" . web-mode)
|
||
("\\.as[cp]x?\\'" . web-mode)
|
||
("\\.eex\\'" . web-mode)
|
||
("\\.erb\\'" . web-mode)
|
||
("\\.mustache\\'" . web-mode)
|
||
("\\.handlebars\\'" . web-mode)
|
||
("\\.hbs\\'" . web-mode)
|
||
("\\.eco\\'" . web-mode)
|
||
("\\.ejs\\'" . web-mode)
|
||
("\\.svelte\\'" . web-mode)
|
||
("\\.ctp\\'" . web-mode)
|
||
("\\.djhtml\\'" . web-mode))
|
||
:general
|
||
(phundrak/major-leader-key
|
||
:keymaps 'web-mode-map
|
||
:packages 'web-mode
|
||
"=" '(nil :which-key "format")
|
||
"E" '(nil :which-key "errors")
|
||
"El" #'web-mode-dom-errors-show
|
||
"gb" #'web-mode-element-beginning
|
||
"g" '(nil :which-key "goto")
|
||
"gc" #'web-mode-element-child
|
||
"gp" #'web-mode-element-parent
|
||
"gs" #'web-mode-element-sibling-next
|
||
"h" '(nil :which-key "dom")
|
||
"hp" #'web-mode-dom-xpath
|
||
"r" '(nil :which-key "refactor")
|
||
"rc" #'web-mode-element-clone
|
||
"rd" #'web-mode-element-vanish
|
||
"rk" #'web-mode-element-kill
|
||
"rr" #'web-mode-element-rename
|
||
"rw" #'web-mode-element-wrap
|
||
"z" #'web-mode-fold-or-unfold))
|
||
#+end_src
|
||
|
||
***** Javascript
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Programming-languages-General-Programming-Languages-Web-programming-Javascript-8k5arup085j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
(use-package rjsx-mode
|
||
:defer t
|
||
:straight (:build t)
|
||
:mode "\\.[mc]?js\\'"
|
||
:mode "\\.es6\\'"
|
||
:mode "\\.pac\\'"
|
||
:interpreter "node"
|
||
:hook (rjsx-mode . rainbow-delimiters-mode)
|
||
:init
|
||
(add-to-list 'compilation-error-regexp-alist 'node)
|
||
(add-to-list 'compilation-error-regexp-alist-alist
|
||
'(node "^[[:blank:]]*at \\(.*(\\|\\)\\(.+?\\):\\([[:digit:]]+\\):\\([[:digit:]]+\\)"
|
||
2 3 4))
|
||
:config
|
||
(setq js-chain-indent t
|
||
js2-basic-offset 2
|
||
;; ignore shebangs
|
||
js2-skip-preprocessor-directives t
|
||
;; Flycheck handles this already
|
||
js2-mode-show-parse-errors nil
|
||
js2-mode-show-strict-warnings nil
|
||
;; conflicting with eslint, Flycheck already handles this
|
||
js2-strict-missing-semi-warning nil
|
||
js2-highlight-level 3
|
||
js2-idle-timer-delay 0.15))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package js2-refactor
|
||
:defer t
|
||
:straight (:build t)
|
||
:after (js2-mode rjsx-mode)
|
||
:hook (js2-mode . js2-refactor-mode)
|
||
:hoo k (rjsx-mode . js2-refactor-mode))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package npm-mode
|
||
:defer t
|
||
:straight (:build t)
|
||
:hook (js-mode . npm-mode)
|
||
:general
|
||
(phundrak/major-leader-key
|
||
:packages '(npm-mode rjsx-mode)
|
||
:keymaps 'rjsx-mode-map
|
||
"n" '(:keymap npm-mode-command-keymap :which-key "npm")))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package prettier-js
|
||
:defer t
|
||
:straight (:build t))
|
||
#+end_src
|
||
|
||
***** CSS
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Programming-languages-General-Programming-Languages-Web-programming-CSS-que40po085j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
;; (add-hook 'css-mode-hook #'smartparens-mode)
|
||
;; (put 'css-indent-offset 'safe-local-variable #'integerp)
|
||
;; (after! css-mode
|
||
;; (phundrak/major-leader-key
|
||
;; :keymaps '(css-mode-map)
|
||
;; "=" '(:ignore :wk "format")
|
||
;; "g" '(:ignore :wk "goto")))
|
||
|
||
(use-package css-mode
|
||
:defer t
|
||
:straight (:type built-in)
|
||
:hook (css-mode . smartparens-mode)
|
||
:init
|
||
(put 'css-indent-offset 'safe-local-variable #'integerp)
|
||
:general
|
||
(phundrak/major-leader-key
|
||
:keymaps 'css-mode-map
|
||
:packages 'css-mode
|
||
"=" '(:ignore :wk "format")
|
||
"g" '(:ignore :wk "goto")))
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package scss-mode
|
||
:straight (:build t)
|
||
:hook (scss-mode . smartparens-mode)
|
||
:defer t
|
||
:mode "\\.scss\\'")
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package counsel-css
|
||
:straight (:build t)
|
||
:defer t
|
||
:init
|
||
(cl-loop for (mode-map . mode-hook) in '((css-mode-map . css-mode-hook)
|
||
(scss-mode-map . scss-mode-hook))
|
||
do (add-hook mode-hook #'counsel-css-imenu-setup)
|
||
(phundrak/major-leader-key
|
||
:keymaps mode-map
|
||
"gh" #'counsel-css)))
|
||
#+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)
|
||
(straight-rebuild-all)))))))
|
||
|
||
(setq dashboard-items '((recents . 15)
|
||
(projects . 10)))
|
||
(dashboard-setup-startup-hook)
|
||
:init
|
||
(add-hook 'after-init-hook 'dashboard-refresh-buffer))
|
||
#+end_src
|
||
|
||
*** Fringe
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Basic-configuration-Visual-Configuration-Fringe-glc9ch1195j0
|
||
:END:
|
||
It’s nice to know which lines were modified since the last commit in a
|
||
file.
|
||
#+begin_src emacs-lisp
|
||
(use-package git-gutter-fringe
|
||
:straight (:build t)
|
||
:hook ((prog-mode . git-gutter-mode)
|
||
(org-mode . git-gutter-mode)
|
||
(markdown-mode . git-gutter-mode)
|
||
(latex-mode . git-gutter-mode)))
|
||
#+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
|
||
:straight 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
|
||
|
||
*** Ligatures
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Visual-Configuration-Ligatures-2v50x451v8j0
|
||
:END:
|
||
The font I’m using (see
|
||
§[[#Basic-configuration-Visual-Configuration-Fontsxfkjel6184j0]]) supports
|
||
ligatures, but Emacs in GUI mode does not. And of course, there’s a
|
||
package for that.
|
||
#+begin_src emacs-lisp
|
||
(use-package ligature
|
||
:straight (ligature :type git
|
||
:host github
|
||
:repo "mickeynp/ligature.el"
|
||
:build t)
|
||
:config
|
||
(ligature-set-ligatures 't
|
||
'("www"))
|
||
;; Enable traditional ligature support in eww-mode, if the
|
||
;; `variable-pitch' face supports it
|
||
(ligature-set-ligatures '(eww-mode org-mode elfeed-show-mode)
|
||
'("ff" "fi" "ffi"))
|
||
;; Enable all Cascadia Code ligatures in programming modes
|
||
(ligature-set-ligatures 'prog-mode
|
||
'("|||>" "<|||" "<==>" "<!--" "####" "~~>" "***" "||=" "||>"
|
||
":::" "::=" "=:=" "===" "==>" "=!=" "=>>" "=<<" "=/=" "!=="
|
||
"!!." ">=>" ">>=" ">>>" ">>-" ">->" "->>" "-->" "---" "-<<"
|
||
"<~~" "<~>" "<*>" "<||" "<|>" "<$>" "<==" "<=>" "<=<" "<->"
|
||
"<--" "<-<" "<<=" "<<-" "<<<" "<+>" "</>" "###" "#_(" "..<"
|
||
"..." "+++" "/==" "///" "_|_" "www" "&&" "^=" "~~" "~@" "~="
|
||
"~>" "~-" "**" "*>" "*/" "||" "|}" "|]" "|=" "|>" "|-" "{|"
|
||
"[|" "]#" "::" ":=" ":>" ":<" "$>" "==" "=>" "!=" "!!" ">:"
|
||
">=" ">>" ">-" "-~" "-|" "->" "--" "-<" "<~" "<*" "<|" "<:"
|
||
"<$" "<=" "<>" "<-" "<<" "<+" "</" "#{" "#[" "#:" "#=" "#!"
|
||
"##" "#(" "#?" "#_" "%%" ".=" ".-" ".." ".?" "+>" "++" "?:"
|
||
"?=" "?." "??" ";;" "/*" "/=" "/>" "//" "__" "~~" "(*" "*)"
|
||
"\\\\" "://"))
|
||
(global-ligature-mode t))
|
||
#+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
|
||
|
||
*** Secret mode
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Visual-Configuration-Secret-mode-b2e9hp51v8j0
|
||
:END:
|
||
Sometimes, I want to hide the text displayed by Emacs but not lock
|
||
altogether my computer. In this case, ~secret-mode~ comes in handy.
|
||
#+begin_src emacs-lisp
|
||
(use-package secret-mode
|
||
:defer t
|
||
:straight (secret-mode :build t
|
||
:type git
|
||
:host github
|
||
:repo "bkaestner/secret-mode.el"))
|
||
#+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
|
||
|
||
*** 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:
|
||
*** ArchWiki pages
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Misc-ArchWiki-pages-nha3jhq0r4j0
|
||
:END:
|
||
A small package I’ve written allows the user to view ArchLinux pages
|
||
either in Emacs or in an external web browser. I prefer the defaults.
|
||
#+begin_src emacs-lisp
|
||
(use-package archwiki
|
||
:defer t
|
||
:straight (archwiki :build t
|
||
:type git
|
||
:repo "https://labs.phundrak.com/phundrak/archwiki.el"))
|
||
#+end_src
|
||
|
||
*** ~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
|
||
:straight t
|
||
:config
|
||
(setq avy-keys '(?a ?u ?i ?e ?c ?t ?s ?r ?n))
|
||
:general
|
||
(phundrak/evil
|
||
:packages 'avy
|
||
"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
|
||
|
||
*** Keycast
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Misc-Keycast-nsqgl431t4j0
|
||
:END:
|
||
#+begin_src emacs-lisp
|
||
(use-package keycast
|
||
:defer t
|
||
:straight (:build t)
|
||
:config
|
||
(define-minor-mode keycast-mode
|
||
"Show current command and its key binding in the mode line."
|
||
:global t
|
||
(if keycast-mode
|
||
(add-hook 'pre-command-hook 'keycast--update t)
|
||
(remove-hook 'pre-command-hook 'keycast--update)))
|
||
(add-to-list 'global-mode-string '("" mode-line-keycast " ")))
|
||
#+end_src
|
||
|
||
*** SICP
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Misc-SICP-96u6ukz0l4j0
|
||
:END:
|
||
Who would get interested in Emacs and not want to read the SICP?
|
||
Moreover, inside Emacs?
|
||
#+begin_src emacs-lisp
|
||
(use-package sicp
|
||
:straight (:build t)
|
||
:defer t)
|
||
#+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
|
||
|
||
*** Ytplay
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Packages-Configuration-Misc-Ytplay-wxm9weq0r4j0
|
||
:END:
|
||
~ytplay~ is a small package I’ve written with which you can choose at
|
||
which resolution to play a YouTube video in an external video player.
|
||
#+begin_src emacs-lisp
|
||
(use-package ytplay
|
||
:defer t
|
||
:straight (ytplay :build t
|
||
:type git
|
||
:repo "https://labs.phundrak.com/phundrak/ytplay.el"))
|
||
#+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 'global-map
|
||
"<mouse-2>" nil
|
||
"<mouse-3>" nil)
|
||
|
||
(phundrak/evil
|
||
"U" #'evil-redo
|
||
"C-a" #'beginning-of-line
|
||
"C-e" #'end-of-line
|
||
"C-y" #'yank)
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(phundrak/leader-key
|
||
"SPC" '(counsel-M-x :wk "M-x")
|
||
"'" #'shell-pop
|
||
|
||
"a" '(nil :wk "apps")
|
||
"ac" #'calc
|
||
"ad" #'docker
|
||
"ae" #'elfeed
|
||
"aE" #'eww
|
||
"ak" #'keycast-mode
|
||
"aK" #'keycast-log-mode
|
||
"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 "~/org/config/emacs.org"))
|
||
:wk "emacs.org")
|
||
"ff" #'counsel-find-file
|
||
"fF" #'ivy-quick-find-files
|
||
"fh" #'hexl-find-file
|
||
"fi" '((lambda ()
|
||
(interactive)
|
||
(find-file (concat user-emacs-directory "init.el")))
|
||
:which-key "init.el")
|
||
"fr" #'counsel-recentf
|
||
"fs" #'save-buffer
|
||
"fS" '((lambda ()
|
||
(interactive)
|
||
(find-file "~/org/config/stumpwm.org"))
|
||
:which-key "stumpwm.org")
|
||
|
||
"h" '(nil :wk "help")
|
||
"hk" #'which-key-show-top-level
|
||
"hi" #'info
|
||
"hI" #'info-display-manual
|
||
"hd" '(nil :wk "describe")
|
||
"hdc" #'describe-char
|
||
"hdC" #'helpful-command
|
||
"hdf" #'helpful-callable
|
||
"hdi" #'describe-input-method
|
||
"hdk" #'helpful-key
|
||
"hdm" #'helpful-macro
|
||
"hdM" #'helpful-mode
|
||
"hdp" #'describe-package
|
||
"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" #'kill-buffer-and-delete-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~.
|