[Misc] switching to new repo for org files
This commit is contained in:
377
docs/emacs/basic-config.org
Normal file
377
docs/emacs/basic-config.org
Normal file
@@ -0,0 +1,377 @@
|
||||
#+title: Emacs — Basic Configuration
|
||||
#+setupfile: ../headers
|
||||
#+property: header-args:emacs-lisp :mkdirp yes :lexical t :exports code
|
||||
#+property: header-args:emacs-lisp+ :tangle ~/.config/emacs/lisp/basic-config.el
|
||||
#+property: header-args:emacs-lisp+ :mkdirp yes :noweb no-export
|
||||
|
||||
* Basic Configuration
|
||||
** Early Init
|
||||
:PROPERTIES:
|
||||
:header-args:emacs-lisp: :tangle ~/.config/emacs/early-init.el :mkdirp yes
|
||||
:header-args:emacs-lisp+: :exports code :results silent :lexical t
|
||||
:END:
|
||||
The early init file is the file loaded before anything else in
|
||||
Emacs. This is where I put some options in order to disable as quickly
|
||||
as possible some built-in features of Emacs before they can be even
|
||||
loaded, speeding Emacs up a bit.
|
||||
#+begin_src emacs-lisp :mkdirp yes
|
||||
(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
|
||||
(setq gc-cons-threshold (* 1024 1024 1024))
|
||||
#+end_src
|
||||
|
||||
** Emacs Behavior
|
||||
*** Editing Text in Emacs
|
||||
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
|
||||
Emacs usually considers a word can be understood as several words, as
|
||||
in camelCase words, and allows us to jump words on this finer level.
|
||||
#+begin_src emacs-lisp
|
||||
(global-subword-mode 1)
|
||||
#+end_src
|
||||
|
||||
Changing half my screen each time my cursor goes too high or too low
|
||||
is not exactly ideal. Fortunately, if we set ~scroll-conservatively~
|
||||
high enough we can have the cursor stay on top or at the bottom of the
|
||||
screen while the text scrolls progressively.
|
||||
#+begin_src emacs-lisp
|
||||
(setq scroll-conservatively 1000)
|
||||
#+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
|
||||
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 tangent 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 anyway, so
|
||||
I’ll stick with spaces by default and change it where needed.
|
||||
|
||||
*** Programming Modes
|
||||
First off, my definition of what makes 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
|
||||
#+header: :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
|
||||
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
|
||||
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!
|
||||
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 access.
|
||||
#+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!
|
||||
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
|
||||
Let’s raise Emacs undo memory to 10 MB, and make Emacs auto-save our
|
||||
files by default.
|
||||
#+begin_src emacs-lisp
|
||||
(setq undo-limit 100000000
|
||||
auto-save-default t)
|
||||
#+end_src
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
(setq window-combination-resize t) ; take new window space from all other windows
|
||||
#+end_src
|
||||
|
||||
** Personal Information
|
||||
Emacs needs to know its master! For various reasons by the way, some
|
||||
packages rely on 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
|
||||
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
|
||||
(with-eval-after-load 'mule-util
|
||||
(setq truncate-string-ellipsis "…"))
|
||||
#+end_src
|
||||
|
||||
With Emacs 29.0.50 onwards, a new frame parameter exists:
|
||||
~alpha-background~. Unlike ~alpha~, this frame parameter only makes Emacs’
|
||||
background transparent, excluding images and text.
|
||||
#+begin_src emacs-lisp
|
||||
(add-to-list 'default-frame-alist '(alpha-background . 0.9))
|
||||
#+end_src
|
||||
|
||||
*** Modeline Modules
|
||||
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
|
||||
(require 'time)
|
||||
(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
|
||||
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.")
|
||||
|
||||
(defvar phundrak/default-font-name "Cascadia Code"
|
||||
"Default font.")
|
||||
|
||||
(defun my/set-font ()
|
||||
(when (find-font (font-spec :name phundrak/default-font-name))
|
||||
(set-face-attribute 'default nil
|
||||
:font phundrak/default-font-name
|
||||
:height phundrak/default-font-size)))
|
||||
|
||||
(my/set-font)
|
||||
(add-hook 'server-after-make-frame-hook #'my/set-font)
|
||||
#+end_src
|
||||
|
||||
*** Frame Title
|
||||
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 - Emacs") project-name))))))
|
||||
#+end_src
|
||||
|
||||
** A better custom variable setter
|
||||
Something people often forget about custom variables in Elisp is they
|
||||
can have a custom setter that will run some code if we set the
|
||||
variable properly with ~customize-set-variable~, so ~setq~ shouldn’t be
|
||||
the user’s choice by default. But repeatedly writing
|
||||
~customize-set-variable~ can get tiring and boring. So why not take the
|
||||
best of both world and create ~csetq~, a ~setq~ that uses
|
||||
~customize-set-variable~ under the hood while it keeps a syntax similar
|
||||
to the one ~setq~ uses?
|
||||
#+begin_src emacs-lisp
|
||||
(defmacro csetq (&rest forms)
|
||||
"Bind each custom variable FORM to the value of its VAL.
|
||||
|
||||
FORMS is a list of pairs of values [FORM VAL].
|
||||
`customize-set-variable' is called sequentially on each pairs
|
||||
contained in FORMS. This means `csetq' has a similar behaviour as
|
||||
`setq': each VAL expression are evaluated sequentially, i.e. the
|
||||
first VAL is evaluated before the second, and so on. This means
|
||||
the value of the first FORM can be used to set the second FORM.
|
||||
|
||||
The return value of `csetq' is the value of the last VAL.
|
||||
|
||||
\(fn [FORM VAL]...)"
|
||||
(declare (debug (&rest sexp form))
|
||||
(indent 1))
|
||||
;; Check if we have an even number of arguments
|
||||
(when (= (mod (length forms) 2) 1)
|
||||
(signal 'wrong-number-of-arguments (list 'csetq (1+ (length forms)))))
|
||||
;; Transform FORMS into a list of pairs (FORM . VALUE)
|
||||
(let (sexps)
|
||||
(while forms
|
||||
(let ((form (pop forms))
|
||||
(value (pop forms)))
|
||||
(push `(customize-set-variable ',form ,value)
|
||||
sexps)))
|
||||
`(progn ,@(nreverse sexps))))
|
||||
#+end_src
|
||||
|
||||
I first got inspired by [[https://oremacs.com/2015/01/17/setting-up-ediff/][this blog article]] (archived article, just in
|
||||
case) but it seems the code snippet no longer works properly, so not
|
||||
only did I have to modify it to make it work with an arbitrary amount
|
||||
of arguments (as long as it’s pairs of variables and their value), but
|
||||
I also had to make the code simply work.
|
||||
121
docs/emacs/custom-elisp.org
Normal file
121
docs/emacs/custom-elisp.org
Normal file
@@ -0,0 +1,121 @@
|
||||
#+title: Emacs — Custom Elisp
|
||||
#+setupfile: ../headers
|
||||
#+property: header-args:emacs-lisp :mkdirp yes :lexical t :exports code
|
||||
#+property: header-args:emacs-lisp+ :tangle ~/.config/emacs/lisp/custom-elisp.el
|
||||
#+property: header-args:emacs-lisp+ :mkdirp yes :noweb no-export
|
||||
|
||||
* Custom Elisp
|
||||
** Dired functions
|
||||
*** ~phundrak/open-marked-files~
|
||||
This function allows the user to open all marked files from a dired
|
||||
buffer in 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
|
||||
|
||||
** Switch between buffers
|
||||
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
|
||||
|
||||
** Screenshots
|
||||
Since Emacs27, it is possible for Emacs to take screenshots of itself
|
||||
in various formats. I’m mainly interested in the SVG and PNG format,
|
||||
so I’ll only write functions for these. It isn’t really redundant with
|
||||
the ~screenshot.el~ package used [[file:./packages/applications.md#screenshot][here]] since these functions take a
|
||||
screenshot of Emacs as a whole rather than of a code snippet.
|
||||
|
||||
First, we have a general function which is a slight modification of
|
||||
the function shared by Alphapapa in [[https://www.reddit.com/r/emacs/comments/idz35e/emacs_27_can_take_svg_screenshots_of_itself/g2c2c6y/][this Reddit comment]]. I modified it
|
||||
to make it possible to pass as an argument the format the screenshot
|
||||
will be taken as or ask the user which format they would like to save
|
||||
it as.
|
||||
#+begin_src emacs-lisp
|
||||
(defun self-screenshot (&optional type)
|
||||
"Save a screenshot of type TYPE of the current Emacs frame.
|
||||
As shown by the function `', type can weild the value `svg',
|
||||
`png', `pdf'.
|
||||
|
||||
This function will output in /tmp a file beginning with \"Emacs\"
|
||||
and ending with the extension of the requested TYPE."
|
||||
(interactive (list
|
||||
(intern (completing-read "Screenshot type: "
|
||||
'(png svg pdf postscript)))))
|
||||
(let* ((extension (pcase type
|
||||
('png ".png")
|
||||
('svg ".svg")
|
||||
('pdf ".pdf")
|
||||
('postscript ".ps")
|
||||
(otherwise (error "Cannot export screenshot of type %s" otherwise))))
|
||||
(filename (make-temp-file "Emacs-" nil extension))
|
||||
(data (x-export-frames nil type)))
|
||||
(with-temp-file filename
|
||||
(insert data))
|
||||
(kill-new filename)
|
||||
(message filename)))
|
||||
#+end_src
|
||||
|
||||
I used this function to take the screenshots you can see in this
|
||||
document.
|
||||
|
||||
** Handle new windows
|
||||
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
|
||||
|
||||
** Extend ~add-to-list~
|
||||
One function I find missing regarding ~add-to-list~ is ~add-all-to-list~
|
||||
which enables the user to add multiple elements to a list at once.
|
||||
Instead, with vanilla Emacs, I have to repeatedly call ~add-to-list~.
|
||||
That’s not very clean. Let’s declare this missing function:
|
||||
#+begin_src emacs-lisp
|
||||
(defun add-all-to-list (list-var elements &optional append compare-fn)
|
||||
"Add ELEMENTS to the value of LIST-VAR if it isn’t there yet.
|
||||
|
||||
ELEMENTS is a list of values. For documentation on the variables
|
||||
APPEND and COMPARE-FN, see `add-to-list'."
|
||||
(let (return)
|
||||
(dolist (elt elements return)
|
||||
(setq return (add-to-list list-var elt append compare-fn)))))
|
||||
#+end_src
|
||||
1
docs/emacs/img
Symbolic link
1
docs/emacs/img
Symbolic link
@@ -0,0 +1 @@
|
||||
../img/emacs
|
||||
125
docs/emacs/index.org
Normal file
125
docs/emacs/index.org
Normal file
@@ -0,0 +1,125 @@
|
||||
#+title: Emacs Configuration
|
||||
#+setupfile: ../headers
|
||||
#+property: header-args:emacs-lisp :mkdirp yes :lexical t :exports code
|
||||
#+property: header-args:emacs-lisp+ :tangle ~/.config/emacs/init.el
|
||||
#+property: header-args:emacs-lisp+ :mkdirp yes :noweb no-export
|
||||
|
||||
* Emacs Configuration
|
||||
|
||||
[[file:./img/emacs.svg]]
|
||||
|
||||
** Introduction
|
||||
After a couple of years using Spacemacs and a failed attempt at
|
||||
switching to DoomEmacs, I’m finally switching back to a vanilla
|
||||
configuration! Why? Because I got tired of the framework getting in my
|
||||
way when I wanted to do stuff. I’m sure this is more applicable to
|
||||
Spacemacs than DoomEmacs since the latter has nice macros written to
|
||||
easily add new packages and configure them, such as ~package!~, ~after!~,
|
||||
and others. But ultimately, I wanted to have a system I designed
|
||||
entirely, with the keybinds I want, the packages I want.
|
||||
|
||||
Aso, why Emacs? You know this famous quote:
|
||||
#+begin_quote
|
||||
Emacs is a great operating system, it just lacks a good text editor.
|
||||
#+end_quote
|
||||
|
||||
It’s actually pretty true in my opinion. Emacs is basically a Lisp
|
||||
machine with a default text editor, programmed with EmacsLisp, a
|
||||
general-purpose programming language. Therefore, if you want to do
|
||||
something in Emacs, with enough Elisp you can do it --- if it’s not in
|
||||
Emacs already, that is.
|
||||
|
||||
#+attr_html: :alt Dammit Emacs… :loading lazy
|
||||
#+caption: [[https://xkcd.com/378/][XKCD n°378]]: Real Programmers
|
||||
[[file:./img/real_programmers.png]]
|
||||
|
||||
** A Warning Before You Proceed
|
||||
:PROPERTIES:
|
||||
:header-args:emacs-lisp: :tangle no
|
||||
:END:
|
||||
This configuration makes heavy use of the [[https://orgmode.org/manual/Noweb-Reference-Syntax.html][noweb]] syntax. This means if
|
||||
you encounter some code that looks ~<<like-this>>~, org-mode will
|
||||
replace this snippet with another code snippet declared elsewhere in
|
||||
my configuration. If you see some code that looks ~<<like-this()>>~,
|
||||
some generating code will run and replace this piece of text with the
|
||||
text generated. A quick example:
|
||||
#+begin_src elisp
|
||||
(defun hello ()
|
||||
<<generate-docstring()>>
|
||||
<<print-hello>>)
|
||||
#+end_src
|
||||
|
||||
Will instead appear as
|
||||
#+begin_src emacs-lisp :noweb yes
|
||||
(defun hello ()
|
||||
<<generate-docstring()>>
|
||||
<<print-hello>>)
|
||||
#+end_src
|
||||
|
||||
This is because I have the block of code below named
|
||||
~generate-docstring~ which generates an output, which replaces its noweb
|
||||
tag. You can recognize noweb snippets generating code with the
|
||||
parenthesis. Often, such blocks aren’t visible in my HTML exports, but
|
||||
you can still see them if you open the actual org source file.
|
||||
#+name: generate-docstring
|
||||
#+begin_src emacs-lisp
|
||||
(concat "\""
|
||||
"Print \\\"Hello World!\\\" in the minibuffer."
|
||||
"\"")
|
||||
#+end_src
|
||||
|
||||
On the other hand, noweb snippets without parenthesis simply replace
|
||||
the snippet with the equivalent named code block. For instance the one
|
||||
below is named ~print-hello~ and is placed as-is in the target source
|
||||
block.
|
||||
#+name: print-hello
|
||||
#+begin_src emacs-lisp
|
||||
(message "Hello World!")
|
||||
#+end_src
|
||||
|
||||
** Loading All Configuration Modules
|
||||
|
||||
#+name: emacs-modules
|
||||
| Module Name | Config Page |
|
||||
|--------------------------+----------------------------------|
|
||||
| =basic-config.el= | [[file:./basic-config.org][Basic Configuration]] |
|
||||
| =custom-elisp.el= | [[file:./custom-elisp.org][Custom Elisp]] |
|
||||
| =package-manager.el= | [[file:./package-manager.org][Package Manager]] |
|
||||
| =keybinding-managemers.el= | [[file:./keybinding-managemers.org][Keybinding Managers]] |
|
||||
| =applications.el= | [[file:./packages/applications.org][Packages — Applications]] |
|
||||
| =autocompletion.el= | [[file:./packages/autocompletion.org][Packages — Autocompletion]] |
|
||||
| =editing.el= | [[file:./packages/editing.org][Packages — Editing]] |
|
||||
| =emacs-builtin.el= | [[file:./packages/emacs-builtin.org][Packages — Emacs Built-ins]] |
|
||||
| =exwm.el= | [[file:./packages/exwm.org][Packages — EXWM]] |
|
||||
| =helpful.el= | [[file:./packages/helpful.org][Packages — Making My Life Easier]] |
|
||||
| =latex.el= | [[file:./packages/latex.org][Packages — LaTeX]] |
|
||||
| =misc.el= | [[file:./packages/misc.org][Packages — Misc]] |
|
||||
| =org.el= | [[file:./packages/org.org][Packages — Org Mode]] |
|
||||
| =programming.el= | [[file:./packages/programming.org][Packages — Programming]] |
|
||||
| =visual-config.el= | [[file:./packages/visual-config.org][Packages — Visual Configuration]] |
|
||||
| =keybindings.el= | [[file:./keybindings.org][Keybindings]] |
|
||||
|
||||
#+name: generate-modules
|
||||
#+begin_src emacs-lisp :tangle no :cache yes :var modules=emacs-modules :exports none
|
||||
(mapconcat (lambda (line)
|
||||
(concat "\"" (string-trim (car line) "=" "=") "\""))
|
||||
modules
|
||||
" ")
|
||||
#+end_src
|
||||
|
||||
#+RESULTS[f76449860408293997e174ff94c743d46951835b]: generate-modules
|
||||
: "basic-config.el" "custom-elisp.el" "package-manager.el" "keybinding-managemers.el" "applications.el" "autocompletion.el" "editing.el" "emacs-builtin.el" "exwm.el" "helpful.el" "latex.el" "misc.el" "org.el" "programming.el" "visual-config.el" "keybindings.el"
|
||||
|
||||
#+begin_src emacs-lisp :noweb yes
|
||||
(dolist (module '(<<generate-modules()>>))
|
||||
(load (expand-file-name module
|
||||
(expand-file-name "lisp" user-emacs-directory))))
|
||||
#+end_src
|
||||
|
||||
* TODOs :noexport:
|
||||
** TODO advise ~evil-insert~ in eshell
|
||||
Advise ~evil-insert~ to go to the end of the buffer while in
|
||||
~eshell-mode~.
|
||||
|
||||
** DONE Get started with org-roam
|
||||
CLOSED: [2023-06-17 Sat 13:38]
|
||||
211
docs/emacs/keybinding-managers.org
Normal file
211
docs/emacs/keybinding-managers.org
Normal file
@@ -0,0 +1,211 @@
|
||||
#+title: Emacs — Keybinding Managers
|
||||
#+setupfile: ../headers
|
||||
#+property: header-args:emacs-lisp :mkdirp yes :lexical t :exports code
|
||||
#+property: header-args:emacs-lisp+ :tangle ~/.config/emacs/lisp/keybinding-managemers.el
|
||||
#+property: header-args:emacs-lisp+ :mkdirp yes :noweb no-export
|
||||
|
||||
* Keybinding Managers
|
||||
** Which-key
|
||||
Which key is, I think, one of my favorite quality of life package.
|
||||
When you begin a keybind, Emacs will show you all keybinds you can
|
||||
follow the first one with in order to form a full keychord. Very
|
||||
useful when you have a lot of keybinds and don’t remember exactly what
|
||||
is what.
|
||||
#+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
|
||||
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
|
||||
: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
|
||||
|
||||
** Evil
|
||||
Evil emulates most of vim’s keybinds, because let’s be honest here,
|
||||
they are much more comfortable than Emacs’.
|
||||
#+begin_src emacs-lisp
|
||||
(use-package evil
|
||||
:straight (:build t)
|
||||
:after (general)
|
||||
:init
|
||||
(setq evil-want-integration t
|
||||
evil-want-keybinding nil
|
||||
evil-want-C-u-scroll t
|
||||
evil-want-C-i-jump nil)
|
||||
(require 'evil-vars)
|
||||
(evil-set-undo-system 'undo-tree)
|
||||
:config
|
||||
<<evil-undefine-keys>>
|
||||
<<evil-bepo>>
|
||||
(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))
|
||||
#+end_src
|
||||
|
||||
I want to undefine some default keybinds of Evil because it does not
|
||||
match my workflow. Namely, I use the space key and the comma as
|
||||
leaders for my keybinds, and I’m way too used to Emacs’ ~C-t~, ~C-a~, ~C-e~,
|
||||
and ~C-y~.
|
||||
#+name: evil-undefine-keys
|
||||
#+begin_src emacs-lisp :tangle no
|
||||
(evil-global-set-key 'motion "t" 'evil-next-visual-line)
|
||||
(evil-global-set-key 'motion "s" 'evil-previous-visual-line)
|
||||
|
||||
(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)
|
||||
#+end_src
|
||||
|
||||
Something else that really bugs me is I use the bépo layout, which is
|
||||
not at all like the qwerty layout. For instance, ~hjkl~ becomes ~ctsr~.
|
||||
Thus, I need some bépo-specific changes.
|
||||
#+name: evil-bepo
|
||||
#+begin_src emacs-lisp :tangle no
|
||||
(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-visual-line
|
||||
"T" 'evil-join
|
||||
"s" 'evil-previous-visual-line
|
||||
"S" 'evil-lookup
|
||||
"r" 'evil-forward-char
|
||||
"R" 'evil-window-bottom)
|
||||
#+end_src
|
||||
|
||||
This package enables and integrates Evil into a lot of different
|
||||
modes, such as org-mode, dired, mu4e, etc. Again, I need some
|
||||
additional code compared to most people due to the bépo layout.
|
||||
#+begin_src emacs-lisp
|
||||
(use-package evil-collection
|
||||
:after evil
|
||||
:straight (:build t)
|
||||
:config
|
||||
;; bépo conversion
|
||||
(defun my/bépo-rotate-evil-collection (_mode mode-keymaps &rest _rest)
|
||||
(evil-collection-translate-key 'normal mode-keymaps
|
||||
;; bépo ctsr is qwerty hjkl
|
||||
"c" "h"
|
||||
"t" "j"
|
||||
"s" "k"
|
||||
"r" "l"
|
||||
;; add back ctsr
|
||||
"h" "c"
|
||||
"j" "t"
|
||||
"k" "s"
|
||||
"l" "r"))
|
||||
(add-hook 'evil-collection-setup-hook #'my/bépo-rotate-evil-collection)
|
||||
(evil-collection-init))
|
||||
#+end_src
|
||||
|
||||
~undo-tree~ is my preferred way of undoing and redoing stuff. The main
|
||||
reason is it doesn’t create a linear undo/redo history, but rather a
|
||||
complete tree you can navigate to see your complete editing history.
|
||||
One of the two obvious things to do are to tell Emacs to save all its
|
||||
undo history fies in a dedicated directory, otherwise we’d risk
|
||||
littering all of our directories. The second thing is to simply
|
||||
globally enable its mode.
|
||||
#+begin_src emacs-lisp
|
||||
(use-package undo-tree
|
||||
:defer t
|
||||
:straight (:build t)
|
||||
:custom
|
||||
(undo-tree-history-directory-alist
|
||||
`(("." . ,(expand-file-name (file-name-as-directory "undo-tree-hist")
|
||||
user-emacs-directory))))
|
||||
:init
|
||||
(global-undo-tree-mode)
|
||||
:config
|
||||
<<undo-tree-ignore-text-properties>>
|
||||
<<undo-tree-compress-files>>
|
||||
(setq undo-tree-visualizer-diff t
|
||||
undo-tree-visualizer-timestamps t
|
||||
undo-tree-auto-save-history t
|
||||
undo-tree-enable-undo-in-region t
|
||||
undo-limit (* 800 1024)
|
||||
undo-strong-limit (* 12 1024 1024)
|
||||
undo-outer-limit (* 128 1024 1024)))
|
||||
#+end_src
|
||||
|
||||
An interesting behavior from DoomEmacs is to compress the history
|
||||
files with ~zstd~ when it is present on the system. Not only do we enjoy
|
||||
much smaller files (according to DoomEmacs, we get something like 80%
|
||||
file savings), Emacs can load them much faster than the regular files.
|
||||
Sure, it uses more CPU time uncompressing these files, but it’s
|
||||
insignificant, and it’s still faster than loading a heavier file.
|
||||
#+name: undo-tree-compress-files
|
||||
#+begin_src emacs-lisp :tangle no
|
||||
(when (executable-find "zstd")
|
||||
(defun my/undo-tree-append-zst-to-filename (filename)
|
||||
"Append .zst to the FILENAME in order to compress it."
|
||||
(concat filename ".zst"))
|
||||
(advice-add 'undo-tree-make-history-save-file-name
|
||||
:filter-return
|
||||
#'my/undo-tree-append-zst-to-filename))
|
||||
#+end_src
|
||||
|
||||
** Hydra
|
||||
[[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
|
||||
391
docs/emacs/keybindings.org
Normal file
391
docs/emacs/keybindings.org
Normal file
@@ -0,0 +1,391 @@
|
||||
#+title: Emacs — Keybindings
|
||||
#+setupfile: ../headers
|
||||
#+property: header-args:emacs-lisp :mkdirp yes :lexical t :exports code
|
||||
#+property: header-args:emacs-lisp+ :tangle ~/.config/emacs/lisp/keybindings.el
|
||||
#+property: header-args:emacs-lisp+ :mkdirp yes :noweb no-export
|
||||
|
||||
* Keybindings
|
||||
Undefining some stuff to make keybind prefixes work correctly.
|
||||
|
||||
#+name: general-keybindings-gen
|
||||
#+header: :tangle no :exports none :results value :cache yes
|
||||
#+begin_src emacs-lisp :var table=keybinds-windows prefix=""
|
||||
(mapconcat (lambda (line)
|
||||
(let* ((key (nth 0 line))
|
||||
(function (nth 1 line))
|
||||
(comment (or (nth 2 line) ""))
|
||||
(package (or (nth 3 line) "")))
|
||||
(format "\"%s%s\" %s"
|
||||
prefix
|
||||
key
|
||||
(if (string= "" comment)
|
||||
(if (member function '("" "nil")) "nil" (concat "#'" function))
|
||||
(format "'(%s :wk %s%s)"
|
||||
(if (member function '("" "nil")) ":ignore t" function)
|
||||
(if (member function '("none" "nil")) "t" (concat "\"" comment "\""))
|
||||
(if (string-blank-p package) "" (concat ":package " package)))))))
|
||||
table
|
||||
"\n")
|
||||
#+end_src
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
(general-define-key
|
||||
:keymaps 'global-map
|
||||
"<mouse-2>" nil
|
||||
"<mouse-3>" nil)
|
||||
|
||||
(phundrak/evil
|
||||
:packages '(counsel)
|
||||
"U" #'evil-redo
|
||||
"C-a" #'beginning-of-line
|
||||
"C-e" #'end-of-line
|
||||
"C-y" #'yank
|
||||
"M-y" #'counsel-yank-pop)
|
||||
#+end_src
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
(general-define-key
|
||||
"<f5>" #'compile
|
||||
"<f6>" #'recompile)
|
||||
|
||||
(phundrak/leader-key
|
||||
"SPC" '(counsel-M-x :wk "M-x")
|
||||
"'" #'shell-pop
|
||||
|
||||
<<general-keybindings-gen(table=keybinds-tabs, prefix="TAB ")>>
|
||||
|
||||
<<general-keybindings-gen(table=keybinds-apps, prefix="a")>>
|
||||
<<general-keybindings-gen(table=keybinds-apps-shell, prefix="as")>>
|
||||
<<general-keybindings-gen(table=treemacs-keybinds, prefix="at")>>
|
||||
|
||||
<<general-keybindings-gen(table=keybinds-buffers, prefix="b")>>
|
||||
|
||||
"c" '(:ignore t :wk "code")
|
||||
"cl" #'evilnc-comment-or-uncomment-lines
|
||||
|
||||
<<keybindings-flycheck>>
|
||||
|
||||
<<general-keybindings-gen(table=keybinds-files, prefix="f")>>
|
||||
<<keybinds-specific-files>>
|
||||
|
||||
<<general-keybindings-gen(table=keybinds-help, prefix="h")>>
|
||||
|
||||
"i" '(:ignore t :wk "insert")
|
||||
"iu" #'counsel-unicode-char
|
||||
|
||||
<<general-keybindings-gen(table=keybinds-jump, prefix="j")>>
|
||||
|
||||
<<general-keybindings-gen(table=keybinds-toggle, prefix="t ")>>
|
||||
|
||||
<<general-keybindings-gen(table=keybinds-text, prefix="T")>>
|
||||
|
||||
<<general-keybindings-gen(table=keybinds-windows, prefix="w")>>
|
||||
|
||||
<<general-keybindings-gen(table=keybinds-quit, prefix="q")>>
|
||||
|
||||
"u" #'universal-argument
|
||||
"U" #'undo-tree-visualize)
|
||||
#+end_src
|
||||
|
||||
** Apps
|
||||
Here are my apps keybinds. Each one of them is prefixed by ~a~.
|
||||
#+name: keybinds-apps
|
||||
| Key | Function | Description | Package |
|
||||
|-----+----------------------------+-------------+------------|
|
||||
| | | apps | |
|
||||
| c | calc | | |
|
||||
| C | | calendar | |
|
||||
| CC | calendar | | |
|
||||
| Co | org-agenda | | org |
|
||||
| Cs | org-caldav-sync | | org-caldav |
|
||||
| d | docker | | |
|
||||
| E | elfeed | | |
|
||||
| e | | email | |
|
||||
| ec | mu4e-compose-new | | |
|
||||
| em | mu4e | | |
|
||||
| k | keycast-mode | | |
|
||||
| K | keycast-log-mode | | |
|
||||
| m | | mastodon | |
|
||||
| mm | mastodon | | mastodon |
|
||||
| mn | mastodon-notifications-get | | mastodon |
|
||||
| mt | mastodon-toot | | mastodon |
|
||||
| T | tetris | | |
|
||||
| w | wttrin | | wttrin |
|
||||
|
||||
I also have two main shell-related functions, prefixed with ~as~.
|
||||
#+name: keybinds-apps-shell
|
||||
| Key | Function | Description | Package |
|
||||
|-----+-------------+-------------+-------------|
|
||||
| | | shells | |
|
||||
| e | eshell-new | | |
|
||||
| v | vterm | | vterm |
|
||||
| V | multi-vterm | | multi-vterm |
|
||||
|
||||
** Buffers
|
||||
My buffer-related keybinds are all prefixed by ~b~.
|
||||
#+name: keybinds-buffers
|
||||
| Key | Function | Description |
|
||||
|-----+------------------------------------+-------------|
|
||||
| | | buffers |
|
||||
| b | bufler-switch-buffer | |
|
||||
| B | bury-buffer | |
|
||||
| c | clone-indirect-buffer | |
|
||||
| C | clone-indirect-buffer-other-window | |
|
||||
| l | bufler | |
|
||||
| d | kill-this-buffer | |
|
||||
| D | kill-buffer | |
|
||||
| h | dashboard-refresh-buffer | |
|
||||
| m | switch-to-messages-buffer | |
|
||||
| n | next-buffer | |
|
||||
| p | previous-buffer | |
|
||||
| r | counsel-buffer-or-recentf | |
|
||||
| s | switch-to-scratch-buffer | |
|
||||
|
||||
** Errors
|
||||
#+begin_src emacs-lisp
|
||||
(defhydra hydra-flycheck
|
||||
(:pre (flycheck-list-errors)
|
||||
:post (quit-windows-on "*Flycheck errors*")
|
||||
:hint nil)
|
||||
("f" flycheck-error-list-set-filter "Filter")
|
||||
("t" flycheck-next-error "Next")
|
||||
("s" flycheck-previous-error "Previous")
|
||||
("gg" flycheck-first-error "First")
|
||||
("G" (progn (goto-char (point-max)) (flycheck-previous-error)) "Last")
|
||||
("q" nil))
|
||||
#+end_src
|
||||
|
||||
#+name: keybindings-flycheck
|
||||
#+begin_src emacs-lisp :tangle no
|
||||
"e" '(:ignore t :which-key "errors")
|
||||
"e." '(hydra-flycheck/body :wk "hydra")
|
||||
"el" #'counsel-flycheck
|
||||
"ee" '(:keymap flycheck-command-map :package 'flycheck :wk "flycheck")
|
||||
"ef" '(:keymap flyspell-mode-map :package 'flyspell :wk "flyspell")
|
||||
"eF" #'flyspell-hydra/body
|
||||
#+end_src
|
||||
|
||||
** Files
|
||||
My keybinds for file manipulation are prefixed by ~f~.
|
||||
#+name: keybinds-files
|
||||
| Key | Function | Description |
|
||||
|-----+----------------------+-------------|
|
||||
| | | files |
|
||||
| f | counsel-find-file | |
|
||||
| F | ivy-quick-find-files | |
|
||||
| h | hexl-find-file | |
|
||||
| r | counsel-recentf | |
|
||||
| s | save-buffer | |
|
||||
|
||||
I also have some keybinds dedicated to opening specific files.
|
||||
#+name: keybinds-specific-files
|
||||
#+begin_src emacs-lisp :tangle no
|
||||
"fc" '((lambda ()
|
||||
(interactive)
|
||||
(find-file "~/org/config/emacs.org"))
|
||||
:wk "emacs.org")
|
||||
"fi" '((lambda ()
|
||||
(interactive)
|
||||
(find-file (concat user-emacs-directory "init.el")))
|
||||
:which-key "init.el")
|
||||
"fR" '((lambda ()
|
||||
(interactive)
|
||||
(counsel-find-file ""
|
||||
(concat user-emacs-directory
|
||||
(file-name-as-directory "straight")
|
||||
(file-name-as-directory "repos"))))
|
||||
:which-key "straight package")
|
||||
"fS" '((lambda ()
|
||||
(interactive)
|
||||
(find-file "~/org/config/stumpwm.org"))
|
||||
:which-key "stumpwm.org")
|
||||
#+end_src
|
||||
|
||||
** Help
|
||||
My keybinds for help are prefixed by ~h~.
|
||||
#+name: keybinds-help
|
||||
| Key | Function | Description |
|
||||
|-----+--------------------------+-------------|
|
||||
| | | help |
|
||||
| k | which-key-show-top-level | |
|
||||
| i | info | |
|
||||
| I | info-display-manual | |
|
||||
| d | | describe |
|
||||
| dc | describe-char | |
|
||||
| dC | helpful-command | |
|
||||
| df | helpful-callable | |
|
||||
| di | describe-input-method | |
|
||||
| dk | helpful-key | |
|
||||
| dm | helpful-macro | |
|
||||
| dM | helpful-mode | |
|
||||
| dp | describe-package | |
|
||||
| ds | helpful-symbol | |
|
||||
| dv | helpful-variable | |
|
||||
|
||||
** Jump
|
||||
My keybinds for jumping around are prefixed by ~j~.
|
||||
#+name: keybinds-jump
|
||||
| Key | Function | Description |
|
||||
|-----+-------------------------+-------------|
|
||||
| | | jump |
|
||||
| f | counsel-file-jump | |
|
||||
| d | dirvish-dwim | |
|
||||
| D | dired-jump-other-window | |
|
||||
|
||||
** Project
|
||||
My keybinds for my projects are prefixed by ~p~.
|
||||
#+name: keybinds-project
|
||||
| Key | Function | Description |
|
||||
|-----+--------------------------------------------+-------------|
|
||||
| | | project |
|
||||
| ! | projectile-run-shell-command-in-root | |
|
||||
| & | projectile-run-async-shell-command-in-root | |
|
||||
| b | counsel-projectile-switch-to-buffer | |
|
||||
| c | counsel-projectile | |
|
||||
| d | counsel-projectile-find-dir | |
|
||||
| e | projectile-edit-dir-locals | |
|
||||
| f | counsel-projectile-find-file | |
|
||||
| g | projectile-find-tag | |
|
||||
| k | project-kill-buffers | |
|
||||
| p | counsel-projectile-switch-project | |
|
||||
| t | ivy-magit-todos | |
|
||||
| v | projectile-vc | |
|
||||
|
||||
*** Treemacs
|
||||
|
||||
#+name: treemacs-keybinds
|
||||
| Key | Function | Description |
|
||||
|-----+----------------------------------------+-------------|
|
||||
| | | treemacs |
|
||||
| c | | create |
|
||||
| cd | treemacs-create-dir | |
|
||||
| cf | treemacs-create-file | |
|
||||
| ci | treemacs-create-icon | |
|
||||
| ct | treemacs-create-theme | |
|
||||
| cw | treemacs-create-workspace | |
|
||||
| d | treemacs-delete-file | |
|
||||
| f | | files |
|
||||
| ff | treemacs-find-file | |
|
||||
| ft | treemacs-find-tag | |
|
||||
| l | | lsp |
|
||||
| ls | treemacs-expand-lsp-symbol | |
|
||||
| ld | treemacs-expand-lsp-treemacs-deps | |
|
||||
| lD | treemacs-collapse-lsp-treemacs-deps | |
|
||||
| lS | treemacs-collapse-lsp-symbol | |
|
||||
| p | | projects |
|
||||
| pa | treemacs-add-project-to-workspace | |
|
||||
| pf | treemacs-project-follow-mode | |
|
||||
| pn | treemacs-project-of-node | |
|
||||
| pp | treemacs-project-at-point | |
|
||||
| pr | treemacs-remove-project-from-workspace | |
|
||||
| pt | treemacs-move-project-down | |
|
||||
| ps | treemacs-move-project-up | |
|
||||
| r | | rename |
|
||||
| rf | treemacs-rename-file | |
|
||||
| rp | treemacs-rename-project | |
|
||||
| rr | treemacs-rename | |
|
||||
| rw | treemacs-rename-workspace | |
|
||||
| t | treemacs | |
|
||||
| T | | toggles |
|
||||
| Td | treemacs-toggle-show-dotfiles | |
|
||||
| Tn | treemacs-toggle-node | |
|
||||
| v | | visit node |
|
||||
| va | treemacs-visit-node-ace | |
|
||||
| vc | treemacs-visit-node-close-treemacs | |
|
||||
| vn | treemacs-visit-node-default | |
|
||||
| y | | yank |
|
||||
| ya | treemacs-copy-absolute-path-at-point | |
|
||||
| yp | treemacs-copy-project-path-at-point | |
|
||||
| yr | treemacs-copy-relative-path-at-point | |
|
||||
| yf | treemacs-copy-file | |
|
||||
|
||||
** Tabs
|
||||
Emacs has native tabs available, which can be interesting when working
|
||||
on multiple projects at once between which we may want to switch. Tabs
|
||||
allow the user not to have multiple frames while keeping the
|
||||
advantages of having multiple frames.
|
||||
|
||||
My keybinds are prefixed by ~SPC TAB~.
|
||||
#+name: keybinds-tabs
|
||||
| Key | Function | Description |
|
||||
|-----+--------------+-------------|
|
||||
| | | tabs |
|
||||
| TAB | tab-switch | |
|
||||
| » | tab-next | |
|
||||
| « | tab-previous | |
|
||||
| c | tab-new | |
|
||||
| C | tab-new-to | |
|
||||
| d | tab-close | |
|
||||
| n | tab-next | |
|
||||
| p | tab-previous | |
|
||||
| r | tab-rename | |
|
||||
|
||||
** Text
|
||||
The prefix here is ~T~.
|
||||
#+name: keybinds-text
|
||||
| Key | Function | Description |
|
||||
|-----+----------------------+-------------|
|
||||
| | | text |
|
||||
| e | string-edit-at-point | |
|
||||
| u | downcase-region | |
|
||||
| U | upcase-region | |
|
||||
| z | hydra-zoom/body | |
|
||||
|
||||
** Toggles
|
||||
My toggle keybinds are prefixed by ~t~.
|
||||
#+name: keybinds-toggle
|
||||
| Key | Function | Description |
|
||||
|-----+---------------------------------------+--------------|
|
||||
| | | toggles |
|
||||
| TAB | tab-bar-mode | |
|
||||
| t | my/modify-frame-alpha-background/body | |
|
||||
| T | counsel-load-theme | |
|
||||
| d | | debug |
|
||||
| de | toggle-debug-on-error | |
|
||||
| dq | toggle-debug-on-quit | |
|
||||
| i | | input method |
|
||||
| it | toggle-input-method | |
|
||||
| is | set-input-method | |
|
||||
|
||||
** Windows
|
||||
A couple of keybinds are hidden from which-key, otherwise there’s not
|
||||
much to say. The prefix here is ~w~.
|
||||
#+name: keybinds-windows
|
||||
| Key | Function | Description |
|
||||
|-----+-------------------------------+-------------|
|
||||
| | | windows |
|
||||
| c | evil-window-left | |
|
||||
| t | evil-window-down | |
|
||||
| s | evil-window-up | |
|
||||
| r | evil-window-right | |
|
||||
| . | windows-adjust-size/body | |
|
||||
| - | split-window-below-and-focus | |
|
||||
| / | split-window-right-and-focus | |
|
||||
| $ | winum-select-window-by-number | |
|
||||
| 0 | winum-select-window-0-or-10 | none |
|
||||
| 1 | winum-select-window-1 | none |
|
||||
| 2 | winum-select-window-2 | none |
|
||||
| 3 | winum-select-window-3 | none |
|
||||
| 4 | winum-select-window-4 | none |
|
||||
| 5 | winum-select-window-5 | none |
|
||||
| 6 | winum-select-window-6 | none |
|
||||
| 7 | winum-select-window-7 | none |
|
||||
| 8 | winum-select-window-8 | none |
|
||||
| 9 | winum-select-window-9 | none |
|
||||
| b | kill-buffer-and-delete-window | |
|
||||
| d | delete-window | |
|
||||
| o | other-window | |
|
||||
| D | delete-other-windows | |
|
||||
| w | | writeroom |
|
||||
| w. | writeroom-buffer-width/body | |
|
||||
| ww | writeroom-mode | |
|
||||
|
||||
** Quit
|
||||
Why would I ever use any of these keybinds? They are prefixed with ~q~.
|
||||
#+name: keybinds-quit
|
||||
| Key | Function | Description |
|
||||
|-----+----------------------------+-------------|
|
||||
| | | quit |
|
||||
| f | delete-frame | |
|
||||
| q | save-buffers-kill-terminal | |
|
||||
| Q | kill-emacs | |
|
||||
85
docs/emacs/package-manager.org
Normal file
85
docs/emacs/package-manager.org
Normal file
@@ -0,0 +1,85 @@
|
||||
#+title: Emacs — Package Manager
|
||||
#+setupfile: ../headers
|
||||
#+property: header-args:emacs-lisp :mkdirp yes :lexical t :exports code
|
||||
#+property: header-args:emacs-lisp+ :tangle ~/.config/emacs/lisp/package-manager.el
|
||||
#+property: header-args:emacs-lisp+ :mkdirp yes :noweb no-export
|
||||
|
||||
* Package Manager
|
||||
** Repositories
|
||||
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 stock /elpa/ repository is renamed to
|
||||
/gnu/ 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 are renamed here in order to avoid any confusion between the
|
||||
two of them. Melpa is a community-maintained repository which contains
|
||||
an absurd amount of Emacs packages.
|
||||
#+begin_src emacs-lisp
|
||||
(setq package-archives '(("melpa" . "https://melpa.org/packages/")
|
||||
("gnu" . "https://elpa.gnu.org/packages/")
|
||||
("nongnu" . "https://elpa.nongnu.org/nongnu/")))
|
||||
#+end_src
|
||||
|
||||
** Straight
|
||||
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 also
|
||||
supports ~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.
|
||||
First, let’s bootstrap straight.
|
||||
#+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))
|
||||
#+end_src
|
||||
|
||||
Now, we can refresh our package list in order to be able to install
|
||||
stuff.
|
||||
#+begin_src emacs-lisp
|
||||
(package-initialize)
|
||||
(unless package-archive-contents
|
||||
(package-refresh-contents))
|
||||
|
||||
#+end_src
|
||||
|
||||
From time to time, I fork some packages either because I’m trying to
|
||||
implement something new in said package, or because the package is
|
||||
unmaintained, and I want to continue developing it a bit more. Straight
|
||||
provides a nice feature for using forks of a package with its ~:fork~
|
||||
option. If set to ~t~, then straight will attempt to retrieve the
|
||||
package with the same name but with a different username on the same
|
||||
host. This username is retrieved through the following variable:
|
||||
#+begin_src emacs-lisp
|
||||
(setq straight-host-usernames
|
||||
'((github . "Phundrak")
|
||||
(gitlab . "Phundrak")))
|
||||
#+end_src
|
||||
|
||||
The huge advantage of straight is it clones through git the packages
|
||||
it installs. This means development can be done directly on the
|
||||
downloaded package. However, Forge (a Magit extension for interacting
|
||||
with websites such as GitHub, Gitlab, and such) interacts by default
|
||||
with the forge described by the =origin= remote, which isn’t necessarily
|
||||
the one I want Forge to interact with by default. Therefore,
|
||||
=straight.el= will name all default remotes =straight= to avoid any name
|
||||
collision with my regular development flow.
|
||||
#+begin_src emacs-lisp
|
||||
(setq straight-vc-git-default-remote-name "straight")
|
||||
#+end_src
|
||||
|
||||
We finally come to the ~use-package~ installation. This is done like so:
|
||||
#+begin_src emacs-lisp
|
||||
(straight-use-package '(use-package :build t))
|
||||
(setq use-package-always-ensure t)
|
||||
#+end_src
|
||||
1703
docs/emacs/packages/applications.org
Normal file
1703
docs/emacs/packages/applications.org
Normal file
File diff suppressed because it is too large
Load Diff
310
docs/emacs/packages/autocompletion.org
Normal file
310
docs/emacs/packages/autocompletion.org
Normal file
@@ -0,0 +1,310 @@
|
||||
#+title: Emacs — Packages — Autocompletion
|
||||
#+setupfile: ../../headers
|
||||
#+property: header-args:emacs-lisp :mkdirp yes :lexical t :exports code
|
||||
#+property: header-args:emacs-lisp+ :tangle ~/.config/emacs/lisp/autocompletion.el
|
||||
#+property: header-args:emacs-lisp+ :mkdirp yes :noweb no-export
|
||||
|
||||
* Autocompletion
|
||||
** Code Autocompletion
|
||||
Company is, in my opinion, the best autocompleting engine for Emacs,
|
||||
and it is one of the most popular if not /the/ most popular.
|
||||
#+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
|
||||
company-backends '(company-capf)
|
||||
company-auto-commit nil
|
||||
company-auto-complete-chars nil
|
||||
company-dabbrev-other-buffers nil
|
||||
company-dabbrev-ignore-case nil
|
||||
company-dabbrev-downcase nil))
|
||||
#+end_src
|
||||
|
||||
This package is a backend for company. It emulates
|
||||
~ac-source-dictionary~ by proposing text related to the current
|
||||
major-mode.
|
||||
#+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
|
||||
|
||||
On the other hand, ~company-box~ is a Company front-end which offers
|
||||
colors, icons, documentation and so on. Very nice.
|
||||
|
||||
Declaring all the icons for the variable
|
||||
~company-box-icons-all-the-icons~ is quite verbose in Elisp, so I do it
|
||||
with an org-table.
|
||||
#+name: company-box-icons
|
||||
| Type | Icon | Color |
|
||||
|---------------+--------------------------+--------|
|
||||
| Unknown | find_in_page | purple |
|
||||
| Text | text_fields | green |
|
||||
| Method | functions | red |
|
||||
| Function | functions | red |
|
||||
| Constructor | functions | red |
|
||||
| Field | functions | red |
|
||||
| Variable | adjust | blue |
|
||||
| Class | class | red |
|
||||
| Interface | settings_input_component | red |
|
||||
| Module | view_module | red |
|
||||
| Property | settings | red |
|
||||
| Unit | straighten | red |
|
||||
| Value | filter_1 | red |
|
||||
| Enum | plus_one | red |
|
||||
| Keyword | filter_center_focus | red |
|
||||
| Snippet | short_text | red |
|
||||
| Color | color_lens | red |
|
||||
| File | insert_drive_file | red |
|
||||
| Reference | collections_bookmark | red |
|
||||
| Folder | folder | red |
|
||||
| EnumMember | people | red |
|
||||
| Constant | pause_circle_filled | red |
|
||||
| Struct | streetview | red |
|
||||
| Event | event | red |
|
||||
| Operator | control_point | red |
|
||||
| TypeParameter | class | red |
|
||||
| Template | short_text | green |
|
||||
| ElispFunction | functions | red |
|
||||
| ElispVariable | check_circle | blue |
|
||||
| ElispFeature | stars | orange |
|
||||
| ElispFace | format_paint | pink |
|
||||
|
||||
#+name: gen-company-box-icons
|
||||
#+headers: :tangle no :noweb yes :exports none :cache yes
|
||||
#+header: :wrap "src emacs-lisp :exports none :tangle no"
|
||||
#+begin_src emacs-lisp :var table=company-box-icons
|
||||
(mapconcat (lambda (row)
|
||||
(format "(%s . ,(all-the-icons-material \"%s\" :face 'all-the-icons-%s))"
|
||||
(car row)
|
||||
(cadr row)
|
||||
(caddr row)))
|
||||
table
|
||||
"\n")
|
||||
#+end_src
|
||||
|
||||
#+RESULTS[8ebf4bb3f7f354571a5d42cf58f8b9ba847ba028]: gen-company-box-icons
|
||||
#+begin_src emacs-lisp :exports none :tangle no
|
||||
(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
|
||||
|
||||
#+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))
|
||||
`(
|
||||
<<gen-company-box-icons()>>))))
|
||||
#+end_src
|
||||
|
||||
** Ivy
|
||||
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 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)
|
||||
:init (all-the-icons-ivy-setup)
|
||||
:hook (after-init . all-the-icons-ivy-setup))
|
||||
(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
|
||||
|
||||
Something that can be missing sometimes in Ivy is the ability to
|
||||
select multiple entries at once. For instance, when programming in
|
||||
Java, LPS can offer you to automatically generate the methods ~equals~
|
||||
and ~hashCode~ based on selected members, but with vanilla Ivy, you can
|
||||
only select one. Not really useful. ~ivy-hydra~ is a package that offers
|
||||
a Hydra interface when in Ivy which allows you to select multiple
|
||||
choices among other things.
|
||||
#+begin_src emacs-lisp
|
||||
(use-package ivy-hydra
|
||||
:requires (ivy hydra)
|
||||
:after ivy
|
||||
:straight (:build t))
|
||||
#+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
|
||||
I could almost merge this chapter with the previous one since counsel
|
||||
is a package that provides loads of completion functions for ivy. The
|
||||
ones I find most useful are ~counsel-M-x~ and ~counsel-find-file~.
|
||||
#+begin_src emacs-lisp
|
||||
(use-package counsel
|
||||
:straight t
|
||||
:after recentf
|
||||
: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
|
||||
Yasnippet allows you to insert some pre-made code by just typing a few
|
||||
characters. It can even generate some string with Elisp expressions
|
||||
and ask the user for some input in some precise places.
|
||||
#+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
|
||||
|
||||
Of course, yasnippet wouldn’t be as awesome as it is without pre-made
|
||||
snippets.
|
||||
#+begin_src emacs-lisp
|
||||
(use-package yasnippet-snippets
|
||||
:defer t
|
||||
:after yasnippet
|
||||
:straight (:build t))
|
||||
#+end_src
|
||||
|
||||
Similarly, yatemplate offers pre-made files rather than just strings.
|
||||
That’s still yasnippet by the way.
|
||||
#+begin_src emacs-lisp
|
||||
(use-package yatemplate
|
||||
:defer t
|
||||
:after yasnippet
|
||||
:straight (:build t))
|
||||
#+end_src
|
||||
|
||||
And finally, with ivy you can choose your snippets from a menu if
|
||||
you’re not sure or if you don’t remember what your snippet is.
|
||||
#+begin_src emacs-lisp
|
||||
(use-package ivy-yasnippet
|
||||
:defer t
|
||||
:after (ivy yasnippet)
|
||||
:straight (:build t)
|
||||
:general
|
||||
(phundrak/leader-key
|
||||
:infix "i"
|
||||
:packages 'ivy-yasnippet
|
||||
"y" #'ivy-yasnippet))
|
||||
#+end_src
|
||||
174
docs/emacs/packages/editing.org
Normal file
174
docs/emacs/packages/editing.org
Normal file
@@ -0,0 +1,174 @@
|
||||
#+title: Emacs — Packages — Editing
|
||||
#+setupfile: ../../headers
|
||||
#+property: header-args:emacs-lisp :mkdirp yes :lexical t :exports code
|
||||
#+property: header-args:emacs-lisp+ :tangle ~/.config/emacs/lisp/editing.el
|
||||
#+property: header-args:emacs-lisp+ :mkdirp yes :noweb no-export
|
||||
|
||||
|
||||
* Editing
|
||||
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
|
||||
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)
|
||||
:config
|
||||
(setq atomic-chrome-default-major-mode 'markdown-mode
|
||||
atomic-chrome-url-major-mode-alist `(("github\\.com" . gfm-mode)
|
||||
("gitlab\\.com" . gfm-mode)
|
||||
("labs\\.phundrak\\.com" . markdown-mode)
|
||||
("reddit\\.com" . markdown-mode))))
|
||||
#+end_src
|
||||
|
||||
** Editorconfig
|
||||
Editorconfig is a unified way of passing to your text editor settings
|
||||
everyone working in a repo need to follow. ~.editorconfig~ files work
|
||||
for VSCode users, vim users, Atom users, Sublime users, and of course
|
||||
Emacs users.
|
||||
#+begin_src emacs-lisp
|
||||
(use-package editorconfig
|
||||
:defer t
|
||||
:straight (:build t)
|
||||
:diminish editorconfig-mode
|
||||
:init
|
||||
(editorconfig-mode t))
|
||||
#+end_src
|
||||
|
||||
** Evil Nerd Commenter
|
||||
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
|
||||
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. 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
|
||||
: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)
|
||||
:general
|
||||
(phundrak/leader-key
|
||||
:infix "r"
|
||||
:packages '(iedit evil-iedit-state)
|
||||
"" '(:ignore t :which-key "refactor")
|
||||
"i" #'evil-iedit-state/iedit-mode)
|
||||
(general-define-key
|
||||
:keymaps 'evil-iedit-state-map
|
||||
"c" nil
|
||||
"s" nil
|
||||
"J" nil
|
||||
"S" #'iedit-expand-down-a-line
|
||||
"T" #'iedit-expand-up-a-line
|
||||
"h" #'evil-iedit-state/evil-change
|
||||
"k" #'evil-iedit-state/evil-substitute
|
||||
"K" #'evil-iedit-state/substitute
|
||||
"q" #'evil-iedit-state/quit-iedit-mode))
|
||||
#+end_src
|
||||
|
||||
** Smartparens
|
||||
#+begin_src emacs-lisp
|
||||
(use-package smartparens
|
||||
:straight (:build t)
|
||||
:defer t)
|
||||
#+end_src
|
||||
|
||||
** Parinfer
|
||||
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. Enabling ~parinfer-rust-mode~ should also
|
||||
automatically disable ~smartparens-mode~ in order to avoid conflicting
|
||||
behavior.
|
||||
#+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/"))
|
||||
(add-hook 'parinfer-rust-mode-hook
|
||||
(lambda () (smartparens-mode -1)))
|
||||
:general
|
||||
(phundrak/major-leader-key
|
||||
:keymaps 'parinfer-rust-mode-map
|
||||
"m" #'parinfer-rust-switch-mode
|
||||
"M" #'parinfer-rust-toggle-disable))
|
||||
#+end_src
|
||||
|
||||
** Smartparens
|
||||
~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 (smartparens :build t
|
||||
:type git
|
||||
:host github
|
||||
:repo "Fuco1/smartparens")
|
||||
:hook (prog-mode . smartparens-mode))
|
||||
#+end_src
|
||||
|
||||
** ~string-edit~
|
||||
~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-at-point
|
||||
:defer t
|
||||
:straight (:build t))
|
||||
#+end_src
|
||||
|
||||
** Writeroom
|
||||
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)
|
||||
:init (global-writeroom-mode 1)
|
||||
:config
|
||||
(setq writeroom-width 100
|
||||
writeroom-fullscreen-effect nil
|
||||
writeroom-maximize-window nil
|
||||
writeroom-mode-line t
|
||||
writeroom-major-modes '(text-mode org-mode markdown-mode nov-mode Info-mode)))
|
||||
#+end_src
|
||||
496
docs/emacs/packages/emacs-builtin.org
Normal file
496
docs/emacs/packages/emacs-builtin.org
Normal file
@@ -0,0 +1,496 @@
|
||||
#+title: Emacs — Packages — Emacs Built-ins
|
||||
#+setupfile: ../../headers
|
||||
#+property: header-args:emacs-lisp :mkdirp yes :lexical t :exports code
|
||||
#+property: header-args:emacs-lisp+ :tangle ~/.config/emacs/lisp/emacs-builtin.el
|
||||
#+property: header-args:emacs-lisp+ :mkdirp yes :noweb no-export
|
||||
|
||||
|
||||
* Emacs built-ins
|
||||
** Dired
|
||||
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.
|
||||
I used to have an extensive configuration for Dired with a couple of
|
||||
additional packages to make it more usable. Dirvish rendered that
|
||||
obsolete!
|
||||
#+begin_src emacs-lisp
|
||||
(use-package dirvish
|
||||
:straight (:build t)
|
||||
:defer t
|
||||
:init (dirvish-override-dired-mode)
|
||||
:custom
|
||||
(dirvish-quick-access-entries
|
||||
'(("h" "~/" "Home")
|
||||
("d" "~/Downloads/" "Downloads")
|
||||
("c" "~/org/config" "Config")
|
||||
("C" "~/Documents/conlanging/content" "Conlanging")))
|
||||
(dirvish-mode-line-format
|
||||
'(:left (sort file-time "" file-size symlink) :right (omit yank index)))
|
||||
(dirvish-attributes '(all-the-icons file-size collapse subtree-state vc-state git-msg))
|
||||
:config
|
||||
(dirvish-peek-mode)
|
||||
<<dired-drag-and-drop>>
|
||||
<<dired-listing-flags>>
|
||||
<<dired-files-and-dirs>>
|
||||
<<dirvish-exa-offload>>
|
||||
(setq dired-dwim-target t
|
||||
dired-recursive-copies 'always
|
||||
dired-recursive-deletes 'top
|
||||
delete-by-moving-to-trash t
|
||||
dirvish-preview-dispatchers (cl-substitute 'pdf-preface 'pdf dirvish-preview-dispatchers))
|
||||
:general
|
||||
(phundrak/evil
|
||||
:keymaps 'dirvish-mode-map
|
||||
:packages '(dired dirvish)
|
||||
"q" #'dirvish-quit
|
||||
"TAB" #'dirvish-subtree-toggle)
|
||||
(phundrak/major-leader-key
|
||||
:keymaps 'dirvish-mode-map
|
||||
:packages '(dired dirvish)
|
||||
"A" #'gnus-dired-attach
|
||||
"a" #'dirvish-quick-access
|
||||
"d" #'dirvish-dispatch
|
||||
"e" #'dirvish-emerge-menu
|
||||
"f" #'dirvish-fd-jump
|
||||
"F" #'dirvish-file-info-menu
|
||||
"h" '(:ignore t :which-key "history")
|
||||
"hp" #'dirvish-history-go-backward
|
||||
"hn" #'dirvish-history-go-forward
|
||||
"hj" #'dirvish-history-jump
|
||||
"hl" #'dirvish-history-last
|
||||
"l" '(:ignore t :which-key "layout")
|
||||
"ls" #'dirvish-layout-switch
|
||||
"lt" #'dirvish-layout-toggle
|
||||
"m" #'dirvish-mark-menu
|
||||
"s" #'dirvish-quicksort
|
||||
"S" #'dirvish-setup-menu
|
||||
"y" #'dirvish-yank-menu
|
||||
"n" #'dirvish-narrow))
|
||||
#+end_src
|
||||
|
||||
It requires some programs which can be installed like so:
|
||||
#+begin_src sh :dir /sudo::~/ :exports code :tangle no :results verbatim
|
||||
pacman -S --needed --noprogressbar --noconfirm --color=never \
|
||||
fd poppler ffmpegthumbnailer mediainfo imagemagick tar unzip
|
||||
#+end_src
|
||||
|
||||
Since Emacs 29, it is possible to enable drag-and-drop between Emacs
|
||||
and other applications.
|
||||
#+name: dired-drag-and-drop
|
||||
#+begin_src emacs-lisp :tangle no
|
||||
(csetq dired-mouse-drag-files t
|
||||
mouse-drag-and-drop-region-cross-program t)
|
||||
#+end_src
|
||||
|
||||
In Dirvish, it’s best to use the long name of flags whenever possible,
|
||||
otherwise some commands won’t work.
|
||||
#+name: dired-listing-flags
|
||||
#+begin_src emacs-lisp :tangle no
|
||||
(csetq dired-listing-switches (string-join '("--all"
|
||||
"--human-readable"
|
||||
"--time-style=long-iso"
|
||||
"--group-directories-first"
|
||||
"-lv1")
|
||||
" "))
|
||||
#+end_src
|
||||
|
||||
However, it is possible to instead use ~exa~ when it is available.
|
||||
Instead of making Emacs’ main thread to the file listing in a
|
||||
directory, we offload it to an external thread.
|
||||
#+name: dirvish-exa-offload
|
||||
#+begin_src emacs-lisp :tangle no
|
||||
(dirvish-define-preview exa (file)
|
||||
"Use `exa' to generate directory preview."
|
||||
:require ("exa")
|
||||
(when (file-directory-p file)
|
||||
`(shell . ("exa" "--color=always" "-al" ,file))))
|
||||
|
||||
(add-to-list 'dirvish-preview-dispatchers 'exa)
|
||||
#+end_src
|
||||
|
||||
Finally, some directories need to be set for Dired to store various
|
||||
files and images.
|
||||
#+name: dired-files-and-dirs
|
||||
#+begin_src emacs-lisp :tangle no
|
||||
(let ((my/file (lambda (path &optional dir)
|
||||
(expand-file-name path (or dir user-emacs-directory))))
|
||||
(my/dir (lambda (path &optional dir)
|
||||
(expand-file-name (file-name-as-directory path)
|
||||
(or dir user-emacs-directory)))))
|
||||
(csetq image-dired-thumb-size 150
|
||||
image-dired-dir (funcall my/dir "dired-img")
|
||||
image-dired-db-file (funcall my/file "dired-db.el")
|
||||
image-dired-gallery-dir (funcall my/dir "gallery")
|
||||
image-dired-temp-image-file (funcall my/file "temp-image" image-dired-dir)
|
||||
image-dired-temp-rotate-image-file (funcall my/file "temp-rotate-image" image-dired-dir)))
|
||||
#+end_src
|
||||
|
||||
Copying files with Dired is a blocking process. It’s usually fine when
|
||||
there’s not a lot to copy, but it becomes annoying when moving larger
|
||||
files. The package ~dired-rsync~ allows copying files with ~rsync~ in the
|
||||
background; we can then carry on with our tasks while the copy is
|
||||
happening.
|
||||
#+begin_src emacs-lisp
|
||||
(use-package dired-rsync
|
||||
:if (executable-find "rsync")
|
||||
:defer t
|
||||
:straight (:build t)
|
||||
:general
|
||||
(phundrak/evil
|
||||
:keymaps 'dired-mode-map
|
||||
:packages 'dired-rsync
|
||||
"C-r" #'dired-rsync))
|
||||
#+end_src
|
||||
|
||||
** Compilation mode
|
||||
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-filter . 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" nil
|
||||
"R" #'recompile
|
||||
"h" nil)
|
||||
(phundrak/leader-key
|
||||
"R" #'recompile)
|
||||
:config
|
||||
(setq compilation-scroll-output t))
|
||||
#+end_src
|
||||
|
||||
** Eshell
|
||||
[[file:../img/emacs-eshell.svg]]
|
||||
|
||||
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)
|
||||
:config
|
||||
(setq eshell-prompt-function
|
||||
(lambda ()
|
||||
(concat (abbreviate-file-name (eshell/pwd))
|
||||
(if (= (user-uid) 0) " # " " λ ")))
|
||||
eshell-prompt-regexp "^[^#λ\n]* [#λ] ")
|
||||
<<eshell-alias-file>>
|
||||
<<eshell-concat-shell-command>>
|
||||
<<eshell-alias-open>>
|
||||
<<eshell-alias-clear>>
|
||||
<<eshell-alias-buffers>>
|
||||
<<eshell-alias-emacs>>
|
||||
<<eshell-alias-mkcd>>
|
||||
:general
|
||||
(phundrak/evil
|
||||
:keymaps 'eshell-mode-map
|
||||
[remap evil-collection-eshell-evil-change] #'evil-backward-char
|
||||
"c" #'evil-backward-char
|
||||
"t" #'evil-next-visual-line
|
||||
"s" #'evil-previous-visual-line
|
||||
"r" #'evil-forward-char
|
||||
"h" #'evil-collection-eshell-evil-change)
|
||||
(general-define-key
|
||||
:keymaps 'eshell-mode-map
|
||||
:states 'insert
|
||||
"C-a" #'eshell-bol
|
||||
"C-e" #'end-of-line))
|
||||
#+end_src
|
||||
|
||||
*** Aliases
|
||||
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-alias" 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."
|
||||
(mapconcat #'identity 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.
|
||||
#+name: eshell-alias-clear
|
||||
#+begin_src emacs-lisp :tangle no
|
||||
(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
|
||||
|
||||
*** Commands
|
||||
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 often use 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
|
||||
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
|
||||
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]]! It’s actually available on
|
||||
MELPA, but since I’m the main dev of this package, I’ll keep track of
|
||||
the git repository.
|
||||
#+begin_src emacs-lisp
|
||||
(use-package eshell-info-banner
|
||||
:after (eshell)
|
||||
:defer t
|
||||
:straight (eshell-info-banner :build t
|
||||
:type git
|
||||
:host github
|
||||
:protocol ssh
|
||||
:repo "phundrak/eshell-info-banner.el")
|
||||
:hook (eshell-banner-load . eshell-info-banner-update-banner)
|
||||
:custom-face
|
||||
(eshell-info-banner-normal-face ((t :foreground "#A3BE8C")))
|
||||
(eshell-info-banner-background-face ((t :foreground "#E5E9F0")))
|
||||
(eshell-info-banner-warning-face ((t :foreround "#D08770")))
|
||||
(eshell-info-banner-critical-face ((t :foreground "#BF616A")))
|
||||
:custom
|
||||
(eshell-info-banner-partition-prefixes (list "/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
|
||||
:if (string= (string-trim (shell-command-to-string "uname -n")) "leon")
|
||||
:load-path "~/fromGIT/emacs-packages/powerline-eshell.el/"
|
||||
:after eshell)
|
||||
#+end_src
|
||||
|
||||
** Eww
|
||||
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
|
||||
|
||||
** Image-mode
|
||||
I won’t modify much for ~image-mode~ (the mode used to display images)
|
||||
aside from Emacs’ ability to use external converters to display some
|
||||
images it wouldn’t be able to handle otherwise.
|
||||
#+begin_src emacs-lisp
|
||||
(setq image-use-external-converter t)
|
||||
#+end_src
|
||||
|
||||
** Info
|
||||
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
|
||||
|
||||
** Tab Bar
|
||||
#+begin_src emacs-lisp
|
||||
(use-package tab-bar
|
||||
:defer t
|
||||
:straight (:type built-in)
|
||||
:custom
|
||||
(tab-bar-close-button-show nil)
|
||||
(tab-bar-new-button-show nil)
|
||||
(tab-bar-new-tab-choice "*dashboard*")
|
||||
:custom-face
|
||||
(tab-bar ((t (:background "#272C36"
|
||||
:foreground "#272C36"
|
||||
:box (:line-width (8 . 5) :style flat-button)))))
|
||||
:init
|
||||
(advice-add #'tab-new
|
||||
:after
|
||||
(lambda (&rest _) (when (y-or-n-p "Rename tab? ")
|
||||
(call-interactively #'tab-rename)))))
|
||||
#+end_src
|
||||
|
||||
** Tramp
|
||||
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
|
||||
(use-package tramp
|
||||
:straight (tramp :type built-in :build t)
|
||||
:config
|
||||
<<tramp-add-yadm>>
|
||||
(csetq tramp-ssh-controlmaster-options nil
|
||||
tramp-verbose 0
|
||||
tramp-auto-save-directory (locate-user-emacs-file "tramp/")
|
||||
tramp-chunksize 2000)
|
||||
(add-to-list 'backup-directory-alist ; deactivate auto-save with TRAMP
|
||||
(cons tramp-file-name-regexp nil)))
|
||||
#+end_src
|
||||
|
||||
*** Yadm
|
||||
[[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
|
||||
401
docs/emacs/packages/exwm.org
Normal file
401
docs/emacs/packages/exwm.org
Normal file
@@ -0,0 +1,401 @@
|
||||
#+title: Emacs — Packages — EXWM
|
||||
#+setupfile: ../../headers
|
||||
#+property: header-args:emacs-lisp :mkdirp yes :lexical t :exports code
|
||||
#+property: header-args:emacs-lisp+ :tangle ~/.config/emacs/lisp/exwm.el
|
||||
#+property: header-args:emacs-lisp+ :mkdirp yes :noweb no-export
|
||||
|
||||
* EXWM
|
||||
So, I’m finally slowly getting back to EXWM. I tried it a couple of
|
||||
years ago, but that was with the SpacemacsOS layer on Spacemacs, on a
|
||||
laptop which got accidentaly formatted before I could save my config
|
||||
and all… So it got me some time to come back. I’m still a bit worried
|
||||
about Emacs being single threaded, so if I get one blocking function
|
||||
blocking Emacs, my whole desktop will hang, but for now I haven’t had
|
||||
this issue.
|
||||
|
||||
All my EXWM config is enabled only if I launch Emacs with the argument
|
||||
~--with-exwm~, otherwise none of the related packages get installed, let
|
||||
alone activated and made available.
|
||||
|
||||
First, I need to install the /X protocol Emacs Lisp Bindings/. It
|
||||
doesn’t seem to be available in any repo, so I’ll install it directly
|
||||
from Git.
|
||||
#+begin_src emacs-lisp
|
||||
(use-package xelb
|
||||
:if (seq-contains-p command-line-args "--with-exwm")
|
||||
:straight (xelb :build t
|
||||
:type git
|
||||
:host github
|
||||
:repo "emacs-straight/xelb"
|
||||
:fork "ch11ng/xelb"))
|
||||
#+end_src
|
||||
|
||||
Next is a function I’ve +stolen+ copied from Daviwil’s [[https://config.daviwil.com/desktop][desktop
|
||||
configuration]]. This allows to launch software in the background
|
||||
easily.
|
||||
#+begin_src emacs-lisp
|
||||
(defun exwm/run-in-background (command &optional once)
|
||||
(let ((command-parts (split-string command " +")))
|
||||
(apply #'call-process `(,(car command-parts) nil 0 nil ,@(cdr command-parts)))))
|
||||
#+end_src
|
||||
|
||||
In order to launch Emacs with EXWM with ~startx~, I need a ~xinit~ file.
|
||||
This one is exported to ~$HOME/.xinitrc.emacs~.
|
||||
#+begin_src sh :tangle ~/.xinitrc.emacs :shebang "#!/bin/sh"
|
||||
xhost +SI:localuser:$USER
|
||||
|
||||
# Set fallback cursor
|
||||
xsetroot -cursor_name left_ptr
|
||||
|
||||
# If Emacs is started in server mode, `emacsclient` is a convenient
|
||||
# way to edit files in place (used by e.g. `git commit`)
|
||||
export VISUAL=emacsclient
|
||||
export EDITOR="$VISUAL"
|
||||
|
||||
# in case Java applications display /nothing/
|
||||
# wmname LG3D
|
||||
# export _JAVA_AWT_WM_NONREPARENTING=1
|
||||
|
||||
autorandr -l home
|
||||
|
||||
exec emacs --with-exwm
|
||||
#+end_src
|
||||
|
||||
** EXWM itself
|
||||
Now we come to the plat de résistance. Like with ~xelb~, I’m using its
|
||||
Git source to install it to make sure I get the right version --- the
|
||||
version available on the GNU ELPA is from the same source, true, but I
|
||||
don’t know at which rate it is updated. And more packages down the
|
||||
line will depend on this Git repository, so I might as well just clone
|
||||
it right now.
|
||||
|
||||
As you can see, I added in the ~:config~ secion to two hooks functions
|
||||
that rename buffers accurately. While the average X window will simply
|
||||
get the name of the current X window, I want Firefox and Qutebrowser
|
||||
to be prefixed with the name of the browser. Actually, all these will
|
||||
be renamed this way:
|
||||
#+name: exwm-renamed-buffers-list
|
||||
- Kitty
|
||||
- Qutebrowser
|
||||
|
||||
#+name: exwm-gen-buffers-rename
|
||||
#+header: :exports none :tangle no
|
||||
#+begin_src emacs-lisp :var buffers=exwm-renamed-buffers-list :cache yes
|
||||
(format "%s\n%S"
|
||||
(mapconcat (lambda (buffer)
|
||||
(let ((buffer-name (if (stringp buffer)
|
||||
buffer
|
||||
(car buffer))))
|
||||
(format "(\"%s\" %S)"
|
||||
(downcase buffer-name)
|
||||
`(exwm-workspace-rename-buffer
|
||||
(concat ,(concat "EXWM: " buffer-name " - ")
|
||||
exwm-title)))))
|
||||
buffers
|
||||
"\n")
|
||||
'(_otherwise (exwm-workspace-rename-buffer exwm-title)))
|
||||
#+end_src
|
||||
|
||||
#+RESULTS[64fdbf1e8957b82aad801ec57f2155a0a8f5be54]: exwm-gen-buffers-rename
|
||||
: ("kitty" (exwm-workspace-rename-buffer (concat "EXWM: Kitty - " exwm-title)))
|
||||
: ("qutebrowser" (exwm-workspace-rename-buffer (concat "EXWM: Qutebrowser - " exwm-title)))
|
||||
: (_otherwise (exwm-workspace-rename-buffer exwm-title))
|
||||
|
||||
#+name: exwm-buffers-name
|
||||
#+begin_src emacs-lisp :tangle no
|
||||
(add-hook 'exwm-update-class-hook
|
||||
(lambda () (exwm-workspace-rename-buffer exwm-class-name)))
|
||||
|
||||
(add-hook 'exwm-update-title-hook
|
||||
(lambda ()
|
||||
(pcase exwm-class-name
|
||||
<<exwm-gen-buffers-rename()>>)))
|
||||
#+end_src
|
||||
|
||||
As you can see below, in the ~:config~ section I added two advices and
|
||||
one hook in order to correctly integrate evil with EXWM. When I’m in
|
||||
an X window, I want to be in insert-mode in order to type however I
|
||||
want. However, when I exit one, I want to default back to normal-mode.
|
||||
#+name: exwm-advices-evil
|
||||
#+begin_src emacs-lisp :tangle no
|
||||
(add-hook 'exwm-manage-finish-hook (lambda () (call-interactively #'exwm-input-release-keyboard)))
|
||||
(advice-add #'exwm-input-grab-keyboard :after (lambda (&optional id) (evil-normal-state)))
|
||||
(advice-add #'exwm-input-release-keyboard :after (lambda (&optional id) (evil-insert-state)))
|
||||
#+end_src
|
||||
|
||||
Secondly, I add ~i~, ~C-SPC~, and ~M-m~ as exwm prefix keys so they aren’t
|
||||
sent directly to the X windows but caught by Emacs (and EXWM). I’ll
|
||||
use the ~i~ key in normal-mode to enter ~insert-mode~ and have Emacs
|
||||
release the keyboard so the X window can grab it. Initially, I had
|
||||
~s-<escape>~ as a keybind for grabbing back the keyboard from an X
|
||||
window, as if I were in insert mode and wanted to go back to normal
|
||||
mode, and I had ~s-I~ to toggle keyboard grabbing. But I found myself
|
||||
more than once trying to use ~s-<escape>~ to toggle this state, ~s-I~
|
||||
completely forgotten. So I removed ~s-I~ and made ~s-<escape>~ behave like
|
||||
~s-I~ once did.
|
||||
#+name: exwm-prefix-keys
|
||||
#+begin_src emacs-lisp :tangle no
|
||||
(general-define-key
|
||||
:keymaps 'exwm-mode-map
|
||||
:states 'normal
|
||||
"i" #'exwm-input-release-keyboard)
|
||||
|
||||
(exwm-input-set-key (kbd "s-<escape>") #'exwm-input-toggle-keyboard)
|
||||
|
||||
(push ?\i exwm-input-prefix-keys)
|
||||
(push (kbd "C-SPC") exwm-input-prefix-keys)
|
||||
(push (kbd "M-m") exwm-input-prefix-keys)
|
||||
#+end_src
|
||||
|
||||
As stated a couple of times in my different configuration files, I’m
|
||||
using the bépo layout, which means the default keys in the number row
|
||||
are laid as follows:
|
||||
#+name: exwm-bepo-number-row
|
||||
#+begin_src emacs-lisp :tangle no
|
||||
(defconst exwm-workspace-keys '("\"" "«" "»" "(" ")" "@" "+" "-" "/" "*"))
|
||||
#+end_src
|
||||
|
||||
With this, we can create keybinds for going or sending X windows to
|
||||
workspaces 0 to 9.
|
||||
#+name: exwm-workspace-keybinds
|
||||
#+begin_src emacs-lisp :tangle no
|
||||
(setq exwm-input-global-keys
|
||||
`(,@exwm-input-global-keys
|
||||
,@(mapcar (lambda (i)
|
||||
`(,(kbd (format "s-%s" (nth i exwm-workspace-keys))) .
|
||||
(lambda ()
|
||||
(interactive)
|
||||
(exwm-workspace-switch-create ,i))))
|
||||
(number-sequence 0 9))
|
||||
,@(mapcar (lambda (i)
|
||||
`(,(kbd (format "s-%d" i)) .
|
||||
(lambda ()
|
||||
(interactive)
|
||||
(exwm-workspace-move-window ,(let ((index (1- i)))
|
||||
(if (< index 0)
|
||||
(- 10 index)
|
||||
;; FIXME: does not work with s-0
|
||||
index))))))
|
||||
(number-sequence 0 9))))
|
||||
#+end_src
|
||||
|
||||
You can then see the list of the keybinds I have set for EXWM, which
|
||||
are all prefixed with ~SPC x~ in normal mode (and ~C-SPC x~ in insert
|
||||
mode), except for ~s-RET~ which opens an eshell terminal.
|
||||
#+name: exwm-keybinds
|
||||
#+begin_src emacs-lisp :tangle no
|
||||
(exwm-input-set-key (kbd "s-<return>") (lambda ()
|
||||
(interactive)
|
||||
(eshell)))
|
||||
|
||||
(phundrak/leader-key
|
||||
:infix "x"
|
||||
"" '(:ignore t :which-key "EXWM")
|
||||
"d" #'exwm-debug
|
||||
"k" #'exwm-input-send-next-key
|
||||
"l" '((lambda ()
|
||||
(interactive)
|
||||
(start-process "" nil "plock"))
|
||||
:which-key "lock")
|
||||
"r" '(:ignore t :wk "rofi")
|
||||
"rr" '((lambda () (interactive)
|
||||
(shell-command "rofi -show drun" (current-buffer) (current-buffer)))
|
||||
:wk "drun")
|
||||
"rw" '((lambda () (interactive)
|
||||
(shell-command "rofi -show window" (current-buffer) (current-buffer)))
|
||||
:wk "windows")
|
||||
"R" '(:ignore t :wk "restart")
|
||||
"Rr" #'exwm-reset
|
||||
"RR" #'exwm-restart
|
||||
"t" '(:ignore t :which-key "toggle")
|
||||
"tf" #'exwm-layout-toggle-fullscreen
|
||||
"tF" #'exwm-floating-toggle-floating
|
||||
"tm" #'exwm-layout-toggle-mode-line
|
||||
"w" '(:ignore t :which-key "workspaces")
|
||||
"wa" #'exwm-workspace-add
|
||||
"wd" #'exwm-workspace-delete
|
||||
"ws" #'exwm-workspace-switch
|
||||
"x" '((lambda ()
|
||||
(interactive)
|
||||
(let ((command (string-trim (read-shell-command "RUN: "))))
|
||||
(start-process command nil command)))
|
||||
:which-key "run")
|
||||
"RET" #'eshell-new)
|
||||
#+end_src
|
||||
|
||||
A couple of commands are also automatically executed through my
|
||||
~autostart~ script written [[file:bin.org::#Autostart-a99e99e7][here]].
|
||||
#+name: exwm-autostart
|
||||
#+begin_src emacs-lisp :tangle no
|
||||
(exwm/run-in-background "autostart")
|
||||
#+end_src
|
||||
|
||||
Finally, let’s only initialize and start EXWM once functions from
|
||||
exwm-randr ran, because otherwise having multiple monitors don’t work.
|
||||
#+name: exwm-init
|
||||
#+begin_src emacs-lisp :tangle no
|
||||
(with-eval-after-load 'exwm-randr
|
||||
(exwm-init))
|
||||
#+end_src
|
||||
|
||||
The complete configuration for the ~exwm~ package can be found below.
|
||||
#+begin_src emacs-lisp :noweb yes
|
||||
(use-package exwm
|
||||
:if (seq-contains-p command-line-args "--with-exwm")
|
||||
:straight (exwm :build t
|
||||
:type git
|
||||
:host github
|
||||
:repo "ch11ng/exwm")
|
||||
:custom
|
||||
(use-dialog-box nil "Disable dialog boxes since they are unusable in EXWM")
|
||||
(exwm-input-line-mode-passthrough t "Pass all keypresses to emacs in line mode.")
|
||||
:init
|
||||
(require 'exwm-config)
|
||||
(setq exwm-workspace-number 6)
|
||||
:config
|
||||
(set-frame-parameter (selected-frame) 'alpha-background 0.7)
|
||||
<<exwm-randr>>
|
||||
|
||||
<<exwm-buffers-name>>
|
||||
|
||||
<<exwm-advices-evil>>
|
||||
<<exwm-prefix-keys>>
|
||||
|
||||
<<exwm-bepo-number-row>>
|
||||
<<exwm-workspace-keybinds>>
|
||||
|
||||
<<exwm-keybinds>>
|
||||
|
||||
<<exwm-autostart>>
|
||||
|
||||
<<exwm-init>>)
|
||||
#+end_src
|
||||
|
||||
** EXWM-Evil integration
|
||||
#+begin_src emacs-lisp
|
||||
(use-package evil-exwm-state
|
||||
:if (seq-contains-p command-line-args "--with-exwm")
|
||||
:defer t
|
||||
:after exwm
|
||||
:straight (evil-exwm-state :build t
|
||||
:type git
|
||||
:host github
|
||||
:repo "domenzain/evil-exwm-state"))
|
||||
#+end_src
|
||||
|
||||
** Multimonitor support
|
||||
#+name: exwm-randr
|
||||
#+begin_src emacs-lisp :tangle no
|
||||
(require 'exwm-randr)
|
||||
(exwm/run-in-background "xwallpaper --zoom \"${cat $HOME/.cache/wallpaper}\"")
|
||||
(start-process-shell-command
|
||||
"xrandr" nil "xrandr --output eDP1 --mode 1920x1080 --pos 2560x0 --rotate normal --output HDMI1 --primary --mode 2560x1080 --pos 0x0 --rotate normal --output VIRTUAL1 --off --output DP-1-0 --off --output DP-1-1 --off")
|
||||
(exwm-randr-enable)
|
||||
(setq exwm-randr-workspace-monitor-plist '(3 "eDP1"))
|
||||
#+end_src
|
||||
|
||||
** Keybinds for a desktop environment
|
||||
#+begin_src emacs-lisp
|
||||
(use-package desktop-environment
|
||||
:defer t
|
||||
:straight (desktop-environment :build t
|
||||
:type git
|
||||
:host github
|
||||
:repo "DamienCassou/desktop-environment")
|
||||
:after exwm
|
||||
:diminish t
|
||||
:config
|
||||
(add-hook 'exwm-init-hook #'desktop-environment-mode)
|
||||
(setq desktop-environment-update-exwm-global-keys :prefix
|
||||
exwm-layout-show-al-buffers t)
|
||||
|
||||
(setq desktop-environment-bluetooth-command "bluetoothctl"
|
||||
|
||||
desktop-environment-brightness-get-command "xbacklight -get"
|
||||
desktop-environment-brightness-get-regexp (rx line-start (group (+ digit)))
|
||||
desktop-environment-brightness-set-command "xbacklight %s"
|
||||
desktop-environment-brightness-normal-increment "-inc 5"
|
||||
desktop-environment-brightness-normal-decrement "-dec 5"
|
||||
desktop-environment-brightness-small-increment "-inc 2"
|
||||
desktop-environment-brightness-small-decrement "-dec 2"
|
||||
|
||||
desktop-environment-volume-normal-decrement "-d 5"
|
||||
desktop-environment-volume-normal-increment "-i 5"
|
||||
desktop-environment-volume-small-decrement "-d 2"
|
||||
desktop-environment-volume-small-increment "-i 2"
|
||||
desktop-environment-volume-set-command "pamixer -u %s"
|
||||
|
||||
desktop-environment-screenshot-directory "~/Pictures/Screenshots"
|
||||
desktop-environment-screenlock-command "plock"
|
||||
|
||||
desktop-environment-music-toggle-command "mpc toggle"
|
||||
desktop-environment-music-previous-command "mpc prev"
|
||||
desktop-environment-music-next-command "mpc next"
|
||||
desktop-environment-music-stop-command "mpc stop")
|
||||
|
||||
(general-define-key
|
||||
"<XF86AudioPause>" (lambda () (interactive)
|
||||
(with-temp-buffer
|
||||
(shell-command "mpc pause" (current-buffer) (current-buffer)))))
|
||||
|
||||
(desktop-environment-mode))
|
||||
#+end_src
|
||||
|
||||
** Bluetooth
|
||||
#+begin_src emacs-lisp
|
||||
(defvar bluetooth-command "bluetoothctl")
|
||||
#+end_src
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
(defun bluetooth-turn-on ()
|
||||
(interactive)
|
||||
(let ((process-connection-type nil))
|
||||
(start-process "" nil bluetooth-command "power" "on")))
|
||||
#+end_src
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
(defun bluetooth-turn-off ()
|
||||
(interactive)
|
||||
(let ((process-connection-type nil))
|
||||
(start-process "" nil bluetooth-command "power" "off")))
|
||||
#+end_src
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
(defun create-bluetooth-device (raw-name)
|
||||
"Create a bluetooth device cons from RAW NAME.
|
||||
The cons will hold first the MAC address of the device, then its
|
||||
human-friendly name."
|
||||
(let ((split-name (split-string raw-name " " t)))
|
||||
`(,(mapconcat #'identity (cddr split-name) " ") . ,(cadr split-name))))
|
||||
#+end_src
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
(require 'dbus)
|
||||
(defun bluetooth-get-devices ()
|
||||
(let ((bus-list (dbus-introspect-get-node-names :system "org.bluez" "/org/bluez/hci0")))
|
||||
(mapcar (lambda (device)
|
||||
`(,(dbus-get-property :system
|
||||
"org.bluez"
|
||||
(concat "/org/bluez/hci0/" device)
|
||||
"org.bluez.Device1"
|
||||
"Alias")
|
||||
. ,device))
|
||||
bus-list)))
|
||||
#+end_src
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
(defun bluetooth-connect-device ()
|
||||
(interactive)
|
||||
(progn
|
||||
(bluetooth-turn-on)
|
||||
(let* ((devices (bluetooth-get-devices))
|
||||
(device (alist-get (completing-read "Device: " devices)
|
||||
devices nil nil #'string=)))
|
||||
(dbus-call-method-asynchronously
|
||||
:system "org.bluez"
|
||||
(concat "/org/bluez/hci0" device)
|
||||
"org.bluez.Device1"
|
||||
"Connect"
|
||||
(lambda (&optional msg)
|
||||
(when msg (message "%s" msg)))))))
|
||||
#+end_src
|
||||
59
docs/emacs/packages/helpful.org
Normal file
59
docs/emacs/packages/helpful.org
Normal file
@@ -0,0 +1,59 @@
|
||||
#+title: Emacs — Packages — Making My Life Easier
|
||||
#+setupfile: ../../headers
|
||||
#+property: header-args:emacs-lisp :mkdirp yes :lexical t :exports code
|
||||
#+property: header-args:emacs-lisp+ :tangle ~/.config/emacs/lisp/helpful.el
|
||||
#+property: header-args:emacs-lisp+ :mkdirp yes :noweb no-export
|
||||
|
||||
* Making my life easier
|
||||
** Bufler
|
||||
Bufler is a package that organises 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
|
||||
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 #'helpful-callable)
|
||||
(counsel-describe-variable-function #'helpful-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
|
||||
207
docs/emacs/packages/latex.org
Normal file
207
docs/emacs/packages/latex.org
Normal file
@@ -0,0 +1,207 @@
|
||||
#+title: Emacs — Packages — LaTeX
|
||||
#+setupfile: ../../headers
|
||||
#+property: header-args:emacs-lisp :mkdirp yes :lexical t :exports code
|
||||
#+property: header-args:emacs-lisp+ :tangle ~/.config/emacs/lisp/latex.el
|
||||
#+property: header-args:emacs-lisp+ :mkdirp yes :noweb no-export
|
||||
|
||||
* LaTeX
|
||||
#+begin_src emacs-lisp :noweb yes
|
||||
(use-package auctex
|
||||
:defer t
|
||||
:straight (:build t)
|
||||
:hook (tex-mode . lsp-deferred)
|
||||
:hook (latex-mode . lsp-deferred)
|
||||
:init
|
||||
(setq TeX-command-default (if (executable-find "latexmk") "LatexMk" "LaTeX")
|
||||
TeX-engine (if (executable-find "xetex") 'xetex 'default)
|
||||
TeX-auto-save t
|
||||
TeX-parse-self t
|
||||
TeX-syntactic-comment t
|
||||
TeX-auto-local ".auctex-auto"
|
||||
TeX-style-local ".auctex-style"
|
||||
TeX-source-correlate-mode t
|
||||
TeX-source-correlate-method 'synctex
|
||||
TeX-source-correlate-start-server nil
|
||||
TeX-electric-sub-and-superscript t
|
||||
TeX-fill-break-at-separators nil
|
||||
TeX-save-query t)
|
||||
:config
|
||||
<<latex-fontification>>
|
||||
(setq TeX-master t)
|
||||
(setcar (cdr (assoc "Check" TeX-command-list)) "chktex -v6 -H %s")
|
||||
(add-hook 'TeX-mode-hook (lambda ()
|
||||
(setq ispell-parser 'tex
|
||||
fill-nobreak-predicate (cons #'texmathp fill-nobreak-predicate))))
|
||||
(add-hook 'TeX-mode-hook #'visual-line-mode)
|
||||
(add-hook 'TeX-update-style-hook #'rainbow-delimiters-mode)
|
||||
:general
|
||||
(phundrak/major-leader-key
|
||||
:packages 'lsp-mode
|
||||
:keymaps '(latex-mode-map LaTeX-mode-map)
|
||||
"l" '(:keymap lsp-command-map :which-key "lsp"))
|
||||
(phundrak/major-leader-key
|
||||
:packages 'auctex
|
||||
:keymaps '(latex-mode-map LaTeX-mode-map)
|
||||
"v" '(TeX-view :which-key "View")
|
||||
"c" '(TeX-command-run-all :which-key "Compile")
|
||||
"m" '(TeX-command-master :which-key "Run a command")))
|
||||
#+end_src
|
||||
|
||||
From Doom Emacs’ configuration:
|
||||
#+name: latex-fontification
|
||||
#+begin_src emacs-lisp :tangle no
|
||||
(setq font-latex-match-reference-keywords
|
||||
'(;; BibLaTeX.
|
||||
("printbibliography" "[{") ("addbibresource" "[{")
|
||||
;; Standard commands.
|
||||
("cite" "[{") ("citep" "[{")
|
||||
("citet" "[{") ("Cite" "[{")
|
||||
("parencite" "[{") ("Parencite" "[{")
|
||||
("footcite" "[{") ("footcitetext" "[{")
|
||||
;; Style-specific commands.
|
||||
("textcite" "[{") ("Textcite" "[{")
|
||||
("smartcite" "[{") ("Smartcite" "[{")
|
||||
("cite*" "[{") ("parencite*" "[{")
|
||||
("supercite" "[{")
|
||||
;; Qualified citation lists.
|
||||
("cites" "[{") ("Cites" "[{")
|
||||
("parencites" "[{") ("Parencites" "[{")
|
||||
("footcites" "[{") ("footcitetexts" "[{")
|
||||
("smartcites" "[{") ("Smartcites" "[{")
|
||||
("textcites" "[{") ("Textcites" "[{")
|
||||
("supercites" "[{")
|
||||
;; Style-independent commands.
|
||||
("autocite" "[{") ("Autocite" "[{")
|
||||
("autocite*" "[{") ("Autocite*" "[{")
|
||||
("autocites" "[{") ("Autocites" "[{")
|
||||
;; Text commands.
|
||||
("citeauthor" "[{") ("Citeauthor" "[{")
|
||||
("citetitle" "[{") ("citetitle*" "[{")
|
||||
("citeyear" "[{") ("citedate" "[{")
|
||||
("citeurl" "[{")
|
||||
;; Special commands.
|
||||
("fullcite" "[{")
|
||||
;; Cleveref.
|
||||
("cref" "{") ("Cref" "{")
|
||||
("cpageref" "{") ("Cpageref" "{")
|
||||
("cpagerefrange" "{") ("Cpagerefrange" "{")
|
||||
("crefrange" "{") ("Crefrange" "{")
|
||||
("labelcref" "{")))
|
||||
|
||||
(setq font-latex-match-textual-keywords
|
||||
'(;; BibLaTeX brackets.
|
||||
("parentext" "{") ("brackettext" "{")
|
||||
("hybridblockquote" "[{")
|
||||
;; Auxiliary commands.
|
||||
("textelp" "{") ("textelp*" "{")
|
||||
("textins" "{") ("textins*" "{")
|
||||
;; Subcaption.
|
||||
("subcaption" "[{")))
|
||||
|
||||
(setq font-latex-match-variable-keywords
|
||||
'(;; Amsmath.
|
||||
("numberwithin" "{")
|
||||
;; Enumitem.
|
||||
("setlist" "[{") ("setlist*" "[{")
|
||||
("newlist" "{") ("renewlist" "{")
|
||||
("setlistdepth" "{") ("restartlist" "{")
|
||||
("crefname" "{")))
|
||||
#+end_src
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
(use-package tex-mode
|
||||
:defer t
|
||||
:straight (:type built-in)
|
||||
:config
|
||||
(setq LaTeX-section-hook '(LaTeX-section-heading
|
||||
LaTeX-section-title
|
||||
LaTeX-section-toc
|
||||
LaTeX-section-section
|
||||
LaTeX-section-label)
|
||||
LaTeX-fill-break-at-separators nil
|
||||
LaTeX-item-indent 0))
|
||||
#+end_src
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
(use-package preview
|
||||
:defer t
|
||||
:straight (:type built-in)
|
||||
:config
|
||||
(add-hook 'LaTeX-mode-hook #'LaTeX-preview-setup)
|
||||
(setq-default preview-scale 1.4
|
||||
preview-scale-function
|
||||
(lambda () (* (/ 10.0 (preview-document-pt)) preview-scale)))
|
||||
(setq preview-auto-cache-preamble nil)
|
||||
(phundrak/major-leader-key
|
||||
:packages 'auctex
|
||||
:keymaps '(latex-mode-map LaTeX-mode-map)
|
||||
"p" #'preview-at-point
|
||||
"P" #'preview-clearout-at-point))
|
||||
#+end_src
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
(use-package cdlatex
|
||||
:defer t
|
||||
:after auctex
|
||||
:straight (:build t)
|
||||
:hook (LaTeX-mode . cdlatex-mode)
|
||||
:hook (org-mode . org-cdlatex-mode)
|
||||
:config
|
||||
(setq cdlatex-use-dollar-to-ensure-math nil)
|
||||
:general
|
||||
(phundrak/major-leader-key
|
||||
:packages 'cdlatex
|
||||
:keymaps 'cdlatex-mode-map
|
||||
"$" nil
|
||||
"(" nil
|
||||
"{" nil
|
||||
"[" nil
|
||||
"|" nil
|
||||
"<" nil
|
||||
"^" nil
|
||||
"_" nil
|
||||
[(control return)] nil))
|
||||
#+end_src
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
(use-package adaptive-wrap
|
||||
:defer t
|
||||
:after auctex
|
||||
:straight (:build t)
|
||||
:hook (LaTeX-mode . adaptative-wrap-prefix-mode)
|
||||
:init (setq-default adaptative-wrap-extra-indent 0))
|
||||
#+end_src
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
(use-package auctex-latexmk
|
||||
:after auctex
|
||||
:defer t
|
||||
:straight (:build t)
|
||||
:init
|
||||
(setq auctex-latexmk-inherit-TeX-PDF-mode t)
|
||||
(add-hook 'LaTeX-mode (lambda () (setq TeX-command-default "LatexMk")))
|
||||
:config
|
||||
(auctex-latexmk-setup))
|
||||
#+end_src
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
(use-package company-auctex
|
||||
:defer t
|
||||
:after (company auctex)
|
||||
:straight (:build t)
|
||||
:config
|
||||
(company-auctex-init))
|
||||
#+end_src
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
(use-package company-math
|
||||
:defer t
|
||||
:straight (:build t)
|
||||
:after (company auctex)
|
||||
:config
|
||||
(defun my-latex-mode-setup ()
|
||||
(setq-local company-backends
|
||||
(append '((company-math-symbols-latex company-latex-commands))
|
||||
company-backends)))
|
||||
(add-hook 'TeX-mode-hook #'my-latex-mode-setup))
|
||||
#+end_src
|
||||
276
docs/emacs/packages/misc.org
Normal file
276
docs/emacs/packages/misc.org
Normal file
@@ -0,0 +1,276 @@
|
||||
#+title: Emacs — Packages — Misc
|
||||
#+setupfile: ../../headers
|
||||
#+property: header-args:emacs-lisp :mkdirp yes :lexical t :exports code
|
||||
#+property: header-args:emacs-lisp+ :tangle ~/.config/emacs/lisp/misc.el
|
||||
#+property: header-args:emacs-lisp+ :mkdirp yes :noweb no-export
|
||||
|
||||
* Misc
|
||||
** ArchWiki pages
|
||||
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~
|
||||
~avy~ is a really convenient way of jumping around and performing
|
||||
actions on these selections, but I’ll need some configuration to make
|
||||
it bépo-compatible.
|
||||
#+begin_src emacs-lisp
|
||||
(use-package avy
|
||||
:defer t
|
||||
:straight t
|
||||
:config
|
||||
(csetq avy-keys '(?a ?u ?i ?e ?c ?t ?s ?r ?n)
|
||||
avy-dispatch-alist '((?x . avy-action-kill-move)
|
||||
(?X . avy-action-kill-stay)
|
||||
(?T . avy-action-teleport)
|
||||
(?m . avy-action-mark)
|
||||
(?C . avy-action-copy)
|
||||
(?y . avy-action-yank)
|
||||
(?Y . avy-action-yank-line)
|
||||
(?I . avy-action-ispell)
|
||||
(?z . avy-action-zap-to-char)))
|
||||
(defun my/avy-goto-url ()
|
||||
"Jump to url with avy."
|
||||
(interactive)
|
||||
(avy-jump "https?://"))
|
||||
(defun my/avy-open-url ()
|
||||
"Open url selected with avy."
|
||||
(interactive)
|
||||
(my/avy-goto-url)
|
||||
(browse-url-at-point))
|
||||
:general
|
||||
(phundrak/evil
|
||||
:pakages 'avy
|
||||
"gc" #'evil-avy-goto-char-timer
|
||||
"gl" #'evil-avy-goto-line)
|
||||
(phundrak/leader-key
|
||||
:packages 'avy
|
||||
:infix "j"
|
||||
"b" #'avy-pop-mark
|
||||
"c" #'evil-avy-goto-char-timer
|
||||
"l" #'avy-goto-line)
|
||||
(phundrak/leader-key
|
||||
:packages 'avy
|
||||
:infix "A"
|
||||
"c" '(:ignore t :which-key "copy")
|
||||
"cl" #'avy-copy-line
|
||||
"cr" #'avy-copy-region
|
||||
"k" '(:ignore t :which-key "kill")
|
||||
"kl" #'avy-kill-whole-line
|
||||
"kL" #'avy-kill-ring-save-whole-line
|
||||
"kr" #'avy-kill-region
|
||||
"kR" #'avy-kill-ring-save-region
|
||||
"m" '(:ignore t :which-key "move")
|
||||
"ml" #'avy-move-line
|
||||
"mr" #'avy-move-region
|
||||
"mt" #'avy-transpose-lines-in-region
|
||||
"n" #'avy-next
|
||||
"p" #'avy-prev
|
||||
"u" #'my/avy-goto-url
|
||||
"U" #'my/avy-open-url)
|
||||
(phundrak/major-leader-key
|
||||
:packages '(avy org)
|
||||
:keymaps 'org-mode-map
|
||||
"A" '(:ignore t :which-key "avy")
|
||||
"Ar" #'avy-org-refile-as-child
|
||||
"Ah" #'avy-org-goto-heading-timer))
|
||||
#+end_src
|
||||
|
||||
** Calc
|
||||
Let’s give ~calc-mode~ some better defaults.
|
||||
#+begin_src emacs-lisp
|
||||
(setq calc-angle-mode 'rad
|
||||
calc-symbolic-mode t)
|
||||
#+end_src
|
||||
|
||||
** Elcord
|
||||
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
|
||||
(csetq elcord-use-major-mode-as-main-icon t
|
||||
elcord-refresh-rate 5
|
||||
elcord-boring-buffers-regexp-list `("^ "
|
||||
,(rx "*" (+ any) "*")
|
||||
,(rx bol (or "Re: "
|
||||
"Fwd: ")))))
|
||||
#+end_src
|
||||
|
||||
** ~ivy-quick-find-files.el~
|
||||
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/university" . "org"))
|
||||
ivy-quick-find-files-fd-additional-options "-L"))
|
||||
#+end_src
|
||||
|
||||
** Keycast
|
||||
In case I am sharing my screen with people and I want to show which
|
||||
functions are called on my keystrokes since I don’t exactly use
|
||||
standard keybindings.
|
||||
#+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
|
||||
|
||||
** Keyfreq
|
||||
Keyfreq is a package that records all the commands I call from Emacs
|
||||
and builds a heatmap out of it.
|
||||
#+begin_src emacs-lisp
|
||||
(use-package keyfreq
|
||||
:straight (:build t)
|
||||
:init
|
||||
(keyfreq-mode 1)
|
||||
(keyfreq-autosave-mode 1)
|
||||
:config
|
||||
(setq keyfreq-excluded-commands '(self-insert-command org-self-insert-command
|
||||
evil-previous-visual-line evil-next-visual-line
|
||||
ivy-next-line evil-backward-char evil-forward-char
|
||||
evil-next-line evil-previous-line evil-normal-state
|
||||
text-scale-pinch)))
|
||||
#+end_src
|
||||
|
||||
** Mastodon
|
||||
#+begin_src emacs-lisp
|
||||
(use-package mastodon
|
||||
:defer t
|
||||
:ensure t
|
||||
:straight (mastodon :type git
|
||||
:host codeberg
|
||||
:repo "martianh/mastodon.el")
|
||||
:config
|
||||
(setq mastodon-instance-url "https://emacs.ch"
|
||||
mastodon-active-user "phundrak")
|
||||
|
||||
(defun me/mastodon-toot--send-language-if-none ()
|
||||
(unless mastodon-toot--language
|
||||
(mastodon-toot--set-toot-language)))
|
||||
(advice-add #'me/mastodon-toot--send-language-if-none :before #'mastodon-toot--send)
|
||||
:general
|
||||
(phundrak/evil
|
||||
:packages '(mastodon)
|
||||
:keymaps 'mastodon-mode-map
|
||||
"]]" '(mastodon-tl--goto-next-toot :wk "Next status")
|
||||
"[[" '(mastodon-tl--goto-prev-toot :wk "Previous status")
|
||||
"gt" '(mastodon-tl--next-tab-item :wk "Next tab item")
|
||||
"gs" '(mastodon-tl--previous-tab-item :wk "Previous tab item")
|
||||
"»" '(mastodon-tl--goto-next-toot :wk "Next status")
|
||||
"«" '(mastodon-tl--goto-prev-toot :wk "Previous status")
|
||||
"q" #'kill-current-buffer)
|
||||
(phundrak/major-leader-key
|
||||
:package 'mastodon
|
||||
:keymaps 'mastodon-mode-map
|
||||
"#" '(mastodon-tl--get-tag-timeline :wk "Tag timeline")
|
||||
"f" '(mastodon-tl--get-federated-timeline :wk "Federated timeline")
|
||||
"F" '(mastoton-tl--view-filters :wk "Filters")
|
||||
"H" '(mastodon-tl--get-local-timeline :wk "Home timeline")
|
||||
"L" '(mastodon-tl--get-local-timeline :wk "Local timeline")
|
||||
"N" '(mastodon-notifications-get :wk "Notifications")
|
||||
"T" '(mastodon-tl--thread :wk "Thread")
|
||||
"O" '(mastodon-profile--my-profile :wk "My profile")
|
||||
"S" '(mastodon-tl--get-follow-suggestions :wk "Follow suggestions")
|
||||
"a" '(mastodon-profile--get-toot-author :wk "Toot author")
|
||||
"b" '(mastodon-profile--view-bookmarks :wk "Bookmarks")
|
||||
"s" '(mastodon-search--search-query :wk "Search query")
|
||||
"U" '(mastodon-tl--update :wk "Update")
|
||||
|
||||
"t" '(nil :wk "Toots")
|
||||
"tt" '(mastodon-toot :wk "Toot")
|
||||
"tb" '(mastodon-toot--toggle-boost :wk "Boost")
|
||||
"tB" '(mastodon-toot--bookmark-toot-toggle :wk "Bookmark")
|
||||
"td" '(mastodon-toot--delete-toot :wk "Delete")
|
||||
"tD" '(mastodon-toot--delete-and-redraft-toot :wk "Redraft")
|
||||
"tf" '(mastodon-toot--toggle-favourite :wk "Favourite")
|
||||
"tF" '(mastodon-profile--view-favourites :wk "View favourites")
|
||||
"tr" '(mastodon-toot--reply :wk "Reply")
|
||||
"tp" '(mastodon-toot--pin-toot-toggle :wk "Pin")
|
||||
"ts" '(mastodon-tl--toggle-spoiler-text-in-toot :wk "Spoiler")
|
||||
"tu" '(mastodon-toot--copy-toot-url :wk "Copy url")
|
||||
"tv" '(mastodon-tl--poll-vote :wk "Vote on poll")
|
||||
|
||||
"f" '(nil :wk "Follow requests")
|
||||
"fa" '(mastodon-notifications--follow-request-accept :wk "Accept")
|
||||
"fr" '(mastodon-notifications--follow-request-reject :wk "Reject")
|
||||
"fv" '(mastodon-profile--view-follow-requests :wk "View follow requests")
|
||||
|
||||
"u" '(nil :wk "User")
|
||||
"uf" '(mastodon-tl--follow-user :wk "Follow")
|
||||
"uF" '(mastodon-tl--unfollow-user :wk "Unfollow")
|
||||
"ub" '(mastodon-tl--block-user :wk "Block")
|
||||
"uB" '(mastodon-tl--unblock-user :wk "Unblock")
|
||||
"um" '(mastodon-tl--mute-user :wk "Mute")
|
||||
"uM" '(mastodon-tl--unmute-user :wk "Unmute")
|
||||
"un" '(mastodon-profile--update-user-profile-note :wk "Update user profile note")
|
||||
"uu" '(mastodon-profile--show-user :wk "Show user")))
|
||||
#+end_src
|
||||
|
||||
** Mediawiki
|
||||
#+begin_src emacs-lisp
|
||||
(use-package mediawiki
|
||||
:defer t
|
||||
:straight (:build t)
|
||||
:custom
|
||||
(mediawiki-site-alist '(("PhundrakWiki" ; Title
|
||||
"https://wiki.phundrak.com/" ; URL
|
||||
"phundrak" ; username
|
||||
nil ; password
|
||||
nil ; LDAP
|
||||
"Main Page")))) ; Default page
|
||||
#+end_src
|
||||
|
||||
** SICP
|
||||
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
|
||||
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
|
||||
~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
|
||||
1399
docs/emacs/packages/org.org
Normal file
1399
docs/emacs/packages/org.org
Normal file
File diff suppressed because it is too large
Load Diff
1782
docs/emacs/packages/programming.org
Normal file
1782
docs/emacs/packages/programming.org
Normal file
File diff suppressed because it is too large
Load Diff
260
docs/emacs/packages/visual-config.org
Normal file
260
docs/emacs/packages/visual-config.org
Normal file
@@ -0,0 +1,260 @@
|
||||
#+title: Emacs — Packages — Visual Configuration
|
||||
#+setupfile: ../../headers
|
||||
#+property: header-args:emacs-lisp :mkdirp yes :lexical t :exports code
|
||||
#+property: header-args:emacs-lisp+ :tangle ~/.config/emacs/lisp/visual-config.el
|
||||
#+property: header-args:emacs-lisp+ :mkdirp yes :noweb no-export
|
||||
|
||||
* Visual Configuration
|
||||
** Dashboard
|
||||
#+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)
|
||||
(agenda . 10)
|
||||
(projects . 10)))
|
||||
(dashboard-setup-startup-hook)
|
||||
:init
|
||||
(add-hook 'after-init-hook 'dashboard-refresh-buffer))
|
||||
#+end_src
|
||||
|
||||
** Fringe
|
||||
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?
|
||||
/*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~ loads 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! First, let’s declare the general symbols that
|
||||
will be used everywhere.
|
||||
#+begin_src emacs-lisp
|
||||
(defun prog-mode-set-symbols-alist ()
|
||||
(setq prettify-symbols-alist '(("lambda" . ?λ)))
|
||||
(prettify-symbols-mode 1))
|
||||
|
||||
(add-hook 'prog-mode-hook #'prog-mode-set-symbols-alist)
|
||||
#+end_src
|
||||
|
||||
We can now take care of the language-specific symbols. First, let’s
|
||||
declare some symbols for the Lisp languages.
|
||||
#+begin_src emacs-lisp
|
||||
(setq-default lisp-prettify-symbols-alist '(("lambda" . ?λ)
|
||||
("defun" . ?𝑓)
|
||||
("defvar" . ?𝑣)
|
||||
("defcustom" . ?𝑐)
|
||||
("defconst" . ?𝐶)))
|
||||
|
||||
(defun lisp-mode-prettify ()
|
||||
(setq prettify-symbols-alist lisp-prettify-symbols-alist)
|
||||
(prettify-symbols-mode -1)
|
||||
(prettify-symbols-mode 1))
|
||||
|
||||
(dolist (lang '(emacs-lisp lisp common-lisp scheme))
|
||||
(add-hook (intern (format "%S-mode-hook" lang))
|
||||
#'lisp-mode-prettify))
|
||||
#+end_src
|
||||
|
||||
Finally, similar to how ~org-appear~ behaves, let’s show the real string
|
||||
of our symbols when the cursor is on it.
|
||||
#+begin_src emacs-lisp
|
||||
(setq prettify-symbols-unprettify-at-point t)
|
||||
#+end_src
|
||||
|
||||
** Ligatures
|
||||
The font I’m using (see *here*) supports ligatures, but Emacs in GUI
|
||||
mode does not. And of course, there’s a package for that.
|
||||
|
||||
# Insert equivalent of #Basic-configuration-Visual-Configuration-Fontsxfkjel6184j0 in *here*
|
||||
|
||||
#+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
|
||||
The DoomEmacs modeline looks nice in my opinion, let’s use it.
|
||||
#+begin_src emacs-lisp
|
||||
(use-package doom-modeline
|
||||
:straight (:build t)
|
||||
:defer t
|
||||
:init (doom-modeline-mode 1)
|
||||
:config
|
||||
(csetq doom-modeline-height 15
|
||||
doom-modeline-enable-word-count t
|
||||
doom-modeline-continuous-word-count-modes '(markdown-mode gfm-mode org-mode)
|
||||
doom-modeline-mu4e t
|
||||
doom-modeline-env-version t)
|
||||
(mu4e-alert-enable-mode-line-display))
|
||||
#+end_src
|
||||
|
||||
** Pixel-perfect alignment of Markdown and org-mode tables
|
||||
:END:
|
||||
Usually, I have no issue with the alignment of the tables I write in
|
||||
org-mode and (more rarely) Markdown. However, there are occurences
|
||||
where I’ll use a character that does not exactly respect my monospace
|
||||
font, which messes with the alignment of the table (often when I do
|
||||
linguistics stuff). A solution to this is the package ~valign~. A little
|
||||
caveat though, as its name implies ~valign~ helps with vertical
|
||||
alignment. If some lines are too high, they won’t exactly fit. Unless?
|
||||
Unless ~valign-fancy-bar~ is set to ~t~.
|
||||
|
||||
For now, I disabled the hook with org-mode and markdown-mode because
|
||||
it slows down opening these files quite a lot. I’ll re-enable the hook
|
||||
once it is fixed.
|
||||
#+begin_src emacs-lisp
|
||||
(use-package valign
|
||||
:defer t
|
||||
:straight (:build t)
|
||||
:after (org markdown-mode)
|
||||
;; :hook ((org-mode markdown-mode) . valign-mode)
|
||||
:custom ((valign-fancy-bar t)))
|
||||
#+end_src
|
||||
|
||||
** Secret mode
|
||||
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
|
||||
|
||||
** Solaire: Incandescent Emacs
|
||||
A common issue when you have a lot of windows opened in Emacs is
|
||||
sometimes there’s just too much. Is the first window source code? Is
|
||||
the other one just an open email? Oh, let’s not forget the ~*Messages*~
|
||||
buffer open next to another source buffer.
|
||||
|
||||
Solaire-mode applies a subtle but useful tweak to your current color
|
||||
scheme: the background of programming buffers is slightly lighter than
|
||||
the background of other buffers. (Or is it other buffers that have a
|
||||
slightly darker background? I’m not sure.)
|
||||
#+begin_src emacs-lisp
|
||||
(use-package solaire-mode
|
||||
:defer t
|
||||
:straight (:build t)
|
||||
:init (solaire-global-mode +1))
|
||||
#+end_src
|
||||
|
||||
** Theme
|
||||
You may have noticed I use the Nord theme pretty much everywhere on my
|
||||
computer, why not Emacs? In my opinion, its aurora variant is nicer
|
||||
than the default Nord theme since it is richer in colors --- just a
|
||||
personal preference.
|
||||
#+begin_src emacs-lisp
|
||||
(use-package doom-themes
|
||||
:straight (:build t)
|
||||
:defer t
|
||||
:init (load-theme 'doom-nord-aurora t))
|
||||
#+end_src
|
||||
|
||||
** Rainbow Delimiters
|
||||
This makes Lisp especially more readable, but it’s also nice to have
|
||||
for any language that has delimiters like brackets too.
|
||||
#+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/?
|
||||
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
|
||||
Reference in New Issue
Block a user