5459 lines
217 KiB
Org Mode
5459 lines
217 KiB
Org Mode
#+title: Emacs Configuration
|
||
#+setupfile: headers
|
||
#+OPTIONS: auto-id:t
|
||
#+HTML_HEAD_EXTRA: <meta name="description" content="Phundrak’s Emacs Configuration" />
|
||
#+HTML_HEAD_EXTRA: <meta property="og:title" content="Phundrak’s Emacs Configuration" />
|
||
#+HTML_HEAD_EXTRA: <meta property="og:description" content="Phundrak’s Emacs Configuration Detailed" />
|
||
#+PROPERTY: header-args: :mkdirp yes :lexical t
|
||
|
||
* Introduction
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Introduction-f1cbb8bb
|
||
:END:
|
||
This file is the main source file for my Emacs configuration which contains most
|
||
of the user code. It is exported thanks to Emacs’ code tangling from the
|
||
original Org file which you can find on my dotfiles’ repository[fn:1] if you are
|
||
reading the web version of it. You can also find there my ~.spacemacs~[fn:2] and
|
||
its code which isn’t part of the present file. As you can see in my
|
||
~.spacemacs~, at init-time, if Emacs detects the tangled configuration files are
|
||
older than the Org file, then Emacs tangles them again, and then loads them.
|
||
|
||
* Spacemacs layers and packages
|
||
:PROPERTIES:
|
||
:header-args:emacs-lisp: :mkdirp yes :tangle ~/.config/emacs/private/spacemacs-layers.el :exports code :results silent :lexical t
|
||
:CUSTOM_ID: Spacemacs_layers_and_packages-6d318b87
|
||
:END:
|
||
#+BEGIN_SRC emacs-lisp :exports none
|
||
;; -*- lexical-binding: t -*-
|
||
#+END_SRC
|
||
|
||
Here will be our layer configuration set. Everything here is set with a
|
||
~setq-default~ in the ~dotspacemacs/layers~ function like so:
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
(defun dotspacemacs/layers ()
|
||
(setq-default
|
||
;; configuration goes here
|
||
))
|
||
#+END_SRC
|
||
|
||
** General configuration
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Spacemacs_layers_and_packages-General_configuration-66b4311e
|
||
:END:
|
||
First, we need to tell Spacemacs which base distribution we are using. This is a
|
||
layer contained in the directory ~+distribution~. For now, available
|
||
distributions are ~spacemacs-base~ and ~spacemacs~ (the default one).
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-distribution 'spacemacs)
|
||
#+END_SRC
|
||
|
||
We can seet a lazy installation of layers —i.e. layers are installed only when a
|
||
file with a supported type is opened. Possible values are:
|
||
- ~all~ :: will lazy install any layer that support lazy installation even the
|
||
layers listed in ~dotspacemacs-configuration-layers~
|
||
- ~unused~ :: will lazy install only unused layers (i.e. layers not listed in
|
||
the variable ~dotspacemacs-configuration-layers~)
|
||
- ~nil~ :: disables the lazy installation feature and you have to explicitly
|
||
list a layer in the variable ~dotspacemacs-configuration-layers~ to install
|
||
it.
|
||
The default value is ~unused~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-enable-lazy-installation 'lazy)
|
||
#+END_SRC
|
||
|
||
If the following variable is non-nil, Spacemacs will ask for confirmation before
|
||
installing a layer lazily. The default value is ~t~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-ask-for-lazy-installation t)
|
||
#+END_SRC
|
||
|
||
** Package management
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Spacemacs_layers_and_packages-Package_management-0add1a62
|
||
:END:
|
||
It is possible to indicate to Spacemacs a list of additional paths where to look
|
||
for configuration layers. Paths must have a trailing slash, i.e.
|
||
=~/.mycontribs/=. As you can see, I added only one:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-configuration-layer-path
|
||
'("~/fromGIT/emacs-packages"))
|
||
#+END_SRC
|
||
|
||
However, I do have additional packages I installed either from the Elpa or the
|
||
Melpa. These are set in ~dotspacemacs-additional-packages~, a list of additional
|
||
packages that will be installed without being wrapped in a layer. If I need some
|
||
configuration for these packages, then I should consider creating a layer. I can
|
||
also puth the configuration in ~dotspacemacs/user-config~. To use a local
|
||
version of a package, use the ~:location~ property, for instance:
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
'(your-package :location "~/path/to/your-package/")
|
||
#+END_SRC
|
||
|
||
With the variable ~dotspacemacs-additional-packages~, it is possible to install
|
||
extra packages which are not already included in any layers. Dependencies should
|
||
be explicitly included as they won’t be resolved automatically. Here is a table
|
||
of all the extra packages I use:
|
||
#+NAME: extra-packages
|
||
| name of the package | why is it installed |
|
||
|----------------------------+--------------------------------------------------------|
|
||
| caddyfile-mode | Major mode for editing Caddyfiles |
|
||
| dired-git-info | Git information in Dired buffers |
|
||
| diredfl | Extra font lock rules for a more colourful dired |
|
||
| edit-indirect | edit region in separate buffer |
|
||
| elcord | rich integration of Emacs in Discord |
|
||
| eshell-syntax-highlighting | Syntax highlighting for Eshell |
|
||
| info-colors | Extra colors for Emacs's Info-mode |
|
||
| multiple-cursors | I don’t like the layer, I prefer this package alone |
|
||
| ob-latex-as-png | Inline arbitrary LaTeX snippets as PNGs in Emacs |
|
||
| org-sidebar | display on the side the outline of an Org buffer |
|
||
| org-tree-slide | presentation tool for org-mode |
|
||
| outorg | edit comments as Org-mode buffers |
|
||
| ox-ssh | SSH config export for org-mode |
|
||
| pinentry | enter a GPG password from Emacs |
|
||
| nord-theme | An arctic, north-bluish clean and elegant Emacs theme. |
|
||
| s | The long lost Emacs string manipulation library. |
|
||
| sicp | Texinfo version of the [[https://mitpress.mit.edu/sites/default/files/sicp/index.html][SICP]] |
|
||
| visual-fill-column | allow the use of ~fill-column~ in ~visual-line-mode~ |
|
||
| wrap-region | easily wrap region with delimiters |
|
||
| wttrin | weather in Emacs |
|
||
| yasnippet-snippets | snippets for YaSnippet |
|
||
|
||
#+name: make-extra-pkg
|
||
#+begin_src emacs-lisp :var packages=extra-packages[,0] :tangle no :exports none
|
||
(mapcar 'make-symbol packages)
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp :noweb yes :exports none
|
||
(setq-default dotspacemacs-additional-packages '<<make-extra-pkg()>>)
|
||
#+end_src
|
||
|
||
It is possible to also list packages that cannot be updated:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-frozen-packages '(helm-icons))
|
||
#+END_SRC
|
||
|
||
And to list packages which won’t be installed nor loaded:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-excluded-packages '(company-tern))
|
||
#+END_SRC
|
||
|
||
Finally, it is possible to define the behaviour of Spacemacs when installing
|
||
packages. Possible values are:
|
||
- ~used-only~ :: installs only explicitly used packages and deletes any unused
|
||
packages as well as their unused dependencies
|
||
- ~used-but-keep-unused~ :: installs only the used packages but won’t delete
|
||
unused ones
|
||
- ~all~ :: installs *all* packages supported by Spacemacs and never uninstalls
|
||
them.
|
||
The default value is ~used-only~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-install-packages 'used-only)
|
||
#+END_SRC
|
||
|
||
** Layers
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Spacemacs_layers_and_packages-Layers-36e1c90c
|
||
:END:
|
||
All layers are set one variable: ~dotspacemacs-configuration-layers~. This
|
||
variable is a list of layers, some of them will have some custom variables.
|
||
Typically, the variable will be set like so:
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
(setq-default dotspacemacs-configuration-layers
|
||
'(emacs-lisp
|
||
helm
|
||
multiple-cursors
|
||
org
|
||
(shell :variables shell-default-height 30
|
||
shell-default-position 'bottom)
|
||
treemacs))
|
||
#+END_SRC
|
||
|
||
# Don’t delete this code block, it wraps the layers
|
||
#+BEGIN_SRC emacs-lisp :exports none
|
||
(setq-default dotspacemacs-configuration-layers '(
|
||
#+END_SRC
|
||
|
||
*** Checkers
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Spacemacs_layers_and_packages-Layers-Checkers-aefeae26
|
||
:END:
|
||
The two first checkers I use are for spell and syntax checking. ~spell-checking~
|
||
is disabled by default, however it should auto-detect the dictionary to use.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(spell-checking :variables
|
||
spell-checking-enable-by-default nil
|
||
spell-checking-enable-auto-dictionary t)
|
||
syntax-checking
|
||
#+END_SRC
|
||
|
||
*** Completion
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Spacemacs_layers_and_packages-Layers-Completion-883e2b83
|
||
:END:
|
||
~auto-completion~ is a layer enabled in order to provide auto-completion to all
|
||
supported language layers. It is set so that the =RET= key has no behavior with
|
||
this layer, however the =TAB= key cycles between candidates displayed by the
|
||
auto-completion toolbox. I also want the autocompletion to include snippets in
|
||
the popup, and the content of the popup is sorted by usage. It is also disabled
|
||
for two modes: magit and Org.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(auto-completion :variables
|
||
auto-completion-complete-with-key-sequence-delay 0.2
|
||
auto-completion-enable-help-tooltip 'manual
|
||
auto-completion-enable-sort-by-usage t
|
||
:disabled-for
|
||
org
|
||
git)
|
||
#+END_SRC
|
||
~helm~ is also enabled, with its header disabled.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(helm :variables helm-no-header t)
|
||
#+END_SRC
|
||
|
||
*** Email
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Spacemacs_layers_and_packages-Layers-Email-67c16308
|
||
:END:
|
||
I use as my daily Email client ~mu4e~, so let’s enable it and tell Emacs where
|
||
mu4e is installed. I also tell mu4e to use maildirs extensions, use async
|
||
operations, where to keep attachments, and enable the mu4e modeline.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(mu4e :variables
|
||
mu4e-installation-path "/usr/share/emacs/site-lisp"
|
||
mu4e-use-maildirs-extension t
|
||
mu4e-enable-mode-line t
|
||
mu4e-attachment-dir "~/Documents/mu4e")
|
||
#+END_SRC
|
||
|
||
*** Emacs
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Spacemacs_layers_and_packages-Layers-Emacs-8e976e5c
|
||
:END:
|
||
The first layer enabled in this category is ~better-defaults~. I also made it so
|
||
that when a command equivalent to ~C-a~ or ~C-e~ is pressed, the cursor will
|
||
respectively first move to the beginning of code first before going past the
|
||
indentation and to the end of the code before going to the end of the line
|
||
before going over the end of the comments on the same line.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(better-defaults :variables
|
||
better-defaults-move-to-beginning-of-code-first t
|
||
better-defaults-move-to-end-of-code-first t)
|
||
#+END_SRC
|
||
|
||
I also enabled ~ibuffer~ and made it so that buffers are sorted by projects.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(ibuffer :variables
|
||
ibuffer-group-buffers-by 'projects)
|
||
#+END_SRC
|
||
|
||
Most important of all, the ~org~ layer is also enabled. I enabled support for
|
||
Epub exports, Github, Reveal.JS exports, and sticky headers. Project support is
|
||
also enabled through files named ~TODOs.org~. I also set the org-download folder
|
||
for images in =~/Pictures/org/=, and I set the =RET= key to follow org links if
|
||
the cursor is on one.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(org :variables
|
||
org-enable-epub-support t
|
||
org-enable-github-support t
|
||
org-enable-hugo-support t
|
||
org-enable-reveal-js-support t
|
||
org-enable-sticky-header t
|
||
org-enable-appear-support t
|
||
spaceline-org-clock-p t
|
||
org-projectile-file "TODOs.org"
|
||
org-download-image-dir "~/Pictures/org/"
|
||
org-return-follows-link t)
|
||
#+END_SRC
|
||
|
||
The ~semantic~ layer is also enabled.
|
||
#+BEGIN_SRC emacs-lisp
|
||
semantic
|
||
#+END_SRC
|
||
|
||
*** File trees
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Spacemacs_layers_and_packages-Layers-File_trees-5b6ed3e4
|
||
:END:
|
||
In this category, I only enabled one layer: ~treemacs~. In this layer, I set is
|
||
so that treemacs syncs with my current buffer, and it automatically refreshes
|
||
its buffer when there is a change in the part of the file system shown by
|
||
treemacs.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(treemacs :variables
|
||
treemacs-use-follow-mode t
|
||
treemacs-use-filewatch-mode t)
|
||
#+END_SRC
|
||
|
||
*** Fonts
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Spacemacs_layers_and_packages-Layers-Fonts-11de8977
|
||
:END:
|
||
In this category, again, one layer is enabled: ~unicode-fonts~. This layer
|
||
addssupport for the ~unicode-fonts~ package.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(unicode-fonts :variables
|
||
unicode-fonts-enable-ligatures t
|
||
unicode-fonts-ligature-modes '(prog-mode)
|
||
unicode-fonts-ligature-set '("|||>" "<|||" "<==>" "<!--" "####" "~~>" "***" "||=" "||>"
|
||
":::" "::=" "=:=" "===" "==>" "=!=" "=>>" "=<<" "=/=" "!=="
|
||
"!!." ">=>" ">>=" ">>>" ">>-" ">->" "->>" "-->" "---" "-<<"
|
||
"<~~" "<~>" "<*>" "<||" "<|>" "<$>" "<==" "<=>" "<=<" "<->"
|
||
"<--" "<-<" "<<=" "<<-" "<<<" "<+>" "</>" "###" "#_(" "..<"
|
||
"..." "+++" "/==" "///" "_|_" "www" "&&" "^=" "~~" "~@" "~="
|
||
"~>" "~-" "**" "*>" "*/" "||" "|}" "|]" "|=" "|>" "|-" "{|"
|
||
"[|" "]#" "::" ":=" ":>" ":<" "$>" "==" "=>" "!=" "!!" ">:"
|
||
">=" ">>" ">-" "-~" "-|" "->" "--" "-<" "<~" "<*" "<|" "<:"
|
||
"<$" "<=" "<>" "<-" "<<" "<+" "</" "#{" "#[" "#:" "#=" "#!"
|
||
"##" "#(" "#?" "#_" "%%" ".=" ".-" ".." ".?" "+>" "++" "?:"
|
||
"?=" "?." "??" ";;" "/*" "/=" "/>" "//" "__" "~~" "(*" "*)"
|
||
"\\\\" "://"))
|
||
#+END_SRC
|
||
|
||
*** Fun
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Spacemacs_layers_and_packages-Layers-Fun-0728c04c
|
||
:END:
|
||
In this category, I only enabled two layers: ~selectric~ and ~xkcd~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
selectric xkcd
|
||
#+END_SRC
|
||
|
||
*** Internationalization
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Spacemacs_layers_and_packages-Layers-Internationalization-69601ff8
|
||
:END:
|
||
In this category, I enabled the ~keyboard-layout~ layer to enable compatibility
|
||
with the bépo layout. This layer, however, is disabled for magit, Dired and eww.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(keyboard-layout :variables
|
||
kl-layout 'bepo
|
||
kl-disabled-configurations '(magit dired eww))
|
||
#+END_SRC
|
||
|
||
*** Programming languages
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Spacemacs_layers_and_packages-Layers-Programming_languages-4c318b81
|
||
:END:
|
||
**** Domain-specific (DSLs)
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Spacemacs_layers_and_packages-Layers-Programming_languages-Domain-specific_(DSLs)-2c919937
|
||
:END:
|
||
In this category, I enabled support for the ~major-modes~ layer for the Arch
|
||
Linux PKGBUILDs support, ~emacs-lisp~ and ~scheme~ layers, support for the CSV
|
||
format with the ~csv~ layer, the ~yaml~ language, shell scripting languages and
|
||
support for the ~dot~ tool with the ~graphviz~ layer.
|
||
#+BEGIN_SRC emacs-lisp
|
||
major-modes emacs-lisp scheme graphviz yaml shell-scripts
|
||
#+END_SRC
|
||
|
||
I also added support for HTML and CSS with the ~html~ layer, with the web
|
||
formatting tool set to ~web-beautify~, and the LSP layer compatibility enabled
|
||
for CSS, less, SCSS and HTML.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(html :variables
|
||
web-fmt-tool 'web-beautify
|
||
css-enable-lsp t
|
||
less-enable-lsp t
|
||
scss-enable-lsp t
|
||
html-enable-lsp t)
|
||
#+END_SRC
|
||
|
||
The ~json~ layer is also enabled, with the format tool set to ~web-beautify~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(json :variables
|
||
json-fmt-tool 'web-beautify)
|
||
#+END_SRC
|
||
|
||
The LaTeX layer has also been enabled, with its default compiler set to XeLaTeX.
|
||
I also enabled the auto-fill feature, the folding capacity and the “magic”
|
||
symbols.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(latex :variables
|
||
latex-build-command "xelatex"
|
||
latex-enable-auto-fill t
|
||
latex-enable-folding t
|
||
latex-enable-magic t)
|
||
#+END_SRC
|
||
|
||
The Markdown layer has been enabled, with support for live preview with ~vmd~,
|
||
and and automatic MMM-mode generation for C, C++, Python, Rust and Emacs Lisp.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(markdown :variables
|
||
markdown-live-preview-engine 'vmd
|
||
markdown-mmm-auto-modes '("c"
|
||
"c++"
|
||
"python"
|
||
"rust"
|
||
("elisp" "emacs-lisp")))
|
||
#+END_SRC
|
||
|
||
PlantUML is a very useful DSL for creating UML diagrams from some text
|
||
description. As you can see below, this layer will be enabled, both as a
|
||
standalone mode for opening ~.pum~ files, but also for org-mode code blocks.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(plantuml :variables
|
||
plantuml-jar-path "~/.local/bin/plantuml.jar"
|
||
org-plantuml-jar-path "~/.local/bin/plantuml.jar")
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(bibtex :variables
|
||
org-ref-default-bibliography '("~/Documents/Papers/references.bib")
|
||
org-ref-pdf-directory "~/Documents/Papers"
|
||
org-ref-bibliography-notes "~/Documents/Papers/notes.org")
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
csv
|
||
#+END_SRC
|
||
|
||
**** Frameworks
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Spacemacs_layers_and_packages-Layers-Programming_languages-Frameworks-f7c21585
|
||
:END:
|
||
Only one framework support has been enabled so far, and is is for the Django
|
||
framework.
|
||
#+BEGIN_SRC emacs-lisp
|
||
django
|
||
#+END_SRC
|
||
|
||
**** General-purpose
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Spacemacs_layers_and_packages-Layers-Programming_languages-General-purpose-1ed71a5b
|
||
:END:
|
||
Among the layers I activated, the only one without any specific configuration is
|
||
the ~asm~ layer for the Assembly language.
|
||
#+BEGIN_SRC emacs-lisp
|
||
asm
|
||
#+END_SRC
|
||
|
||
Next, you can find the C/C++ layer for which I set the default language for ~.h~
|
||
files to be C. I also enabled support for subprojects and organization of the
|
||
include directives on a file save. I also set a couple of LSP-related variables,
|
||
such as the LSP executable for C/C++ for its CCLS backend and some highlight
|
||
variables.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(c-c++ :variables
|
||
c-c++-default-mode-for-headers 'c-mode
|
||
c-c++-adopt-subprojects t
|
||
c-c++-enable-c++11 t
|
||
c-c++-backend 'lsp-clangd
|
||
c-c++-lsp-enable-semantic-highlight t
|
||
c-c++-lsp-semantic-highlight-method 'overlay
|
||
c-c++-lsp-semantic-highlight-rainbow t
|
||
c++-enable-organize-includes-on-save t)
|
||
#+END_SRC
|
||
|
||
Dart has also been enabled, with a custom path to the SDK of the Dart server,
|
||
and to the LSP server of Dart.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(dart :variables
|
||
lsp-dart-project-sdk-dir "/opt/dart-sdk/"
|
||
lsp-dart-sdk-dir "/opt/dart-sdk/")
|
||
#+END_SRC
|
||
|
||
When it comes to the Python layer, I set its backend and formatter to be bound
|
||
to the LSP layer. Its fill columnn was also set to 80 characters, imports are
|
||
sorted on save, and the tests can be run using either nose.el or pytest.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(python :variables
|
||
python-fill-column 80
|
||
python-test-runner '(pytest nose))
|
||
#+END_SRC
|
||
|
||
With the Rust layer, the only custom configuration set is the backend being
|
||
bound to the LSP layer.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(rust :variables rust-backend 'lsp)
|
||
#+END_SRC
|
||
|
||
As regards the JavaScript layer, I set its backend to the LSP layer, and bound
|
||
its format tool to ~web-beautify~ and its REPL is browser-based. I also want to
|
||
include ~node_modules/.bin~ to be automatically added to the buffer local
|
||
~exec_path~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(javascript :variables
|
||
javascript-backend 'lsp
|
||
javascript-fmt-tool 'web-beautify
|
||
javascript-repl 'skewer
|
||
node-add-modules-path t)
|
||
#+END_SRC
|
||
|
||
Alternatively, I also use Typescript which is a sort of better Javascript as it
|
||
should have been, with the LSP backend.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(typescript :variables
|
||
typescript-backend 'lsp)
|
||
#+END_SRC
|
||
|
||
I am also currently using the Awesome window manager which requires the Lua
|
||
programming language, so here it is.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(lua :variables
|
||
lua-backend 'lsp-emmy
|
||
lua-lsp-emmy-jar-path "~/.config/awesome/EmmyLua-LS-all.jar"
|
||
lua-lsp-emmy-java-path "java"
|
||
lua-lsp-emmy-enable-file-watchers t)
|
||
#+END_SRC
|
||
|
||
Unfortunately, I have to write Java and Scala code for one of my university
|
||
courses, so here are the layers:
|
||
#+BEGIN_SRC emacs-lisp
|
||
java scala
|
||
#+END_SRC
|
||
|
||
*** Readers
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Spacemacs_layers_and_packages-Layers-Readers-65e8e4ae
|
||
:END:
|
||
**** Epub and Pdf readers
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Spacemacs_layers_and_packages-Layers-Readers-Epub_and_Pdf_readers-0ef6688f
|
||
:END:
|
||
In this category, only the ~epub~ and ~pdf~ layers are enabled without any
|
||
special configuration, so I can read these files from Emacs directly.
|
||
#+BEGIN_SRC emacs-lisp
|
||
epub pdf
|
||
#+END_SRC
|
||
|
||
**** Elfeed
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Spacemacs_layers_and_packages-Layers-Readers-Elfeed-78877179
|
||
:END:
|
||
Elfeed is an Emacs feeed and RSS reader which can be managed through org files.
|
||
Actually, through only one file in my case, located in my =~/org= directory.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(elfeed :variables
|
||
rmh-elfeed-org-files '("~/org/elfeed.org"))
|
||
#+END_SRC
|
||
|
||
*** Version control
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Spacemacs_layers_and_packages-Layers-Version_control-bc8e286a
|
||
:END:
|
||
Only the ~git~ layer is enabled in this category.
|
||
#+BEGIN_SRC emacs-lisp
|
||
git
|
||
#+END_SRC
|
||
|
||
*** Themes
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Spacemacs_layers_and_packages-Layers-Themes-d88d1225
|
||
:END:
|
||
Here, the ~colors~ layer is the only one enabled. It activates support for
|
||
identifiers colorization, and strings representing colors.
|
||
#+BEGIN_SRC emacs-lisp
|
||
colors
|
||
#+END_SRC
|
||
|
||
*** Tools
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Spacemacs_layers_and_packages-Layers-Tools-e57e405e
|
||
:END:
|
||
In this category, the first layer to be enabled is the CMake layer for which I
|
||
enabled support for the ~cmake-ide~ package.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(cmake :variables
|
||
cmake-enable-cmake-ide-support t)
|
||
#+END_SRC
|
||
|
||
Next, we have the Docker, Nginx, Pass (the standard Unix password manager),
|
||
Prettier, Systemd, Meson, Imenu-list, Web-beautify, Dap, and Helpful.
|
||
#+BEGIN_SRC emacs-lisp
|
||
dap docker helpful imenu-list meson nginx pass prettier systemd web-beautify
|
||
#+END_SRC
|
||
|
||
Of course, let’s not forget about the awesome LSP layer:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(lsp :variables lsp-lens-enable t
|
||
lsp-use-lsp-ui t
|
||
lsp-rust-server 'rust-analyzer)
|
||
#+END_SRC
|
||
|
||
We also have the RestClient layer enabled for which I enabled the Org
|
||
compatibility support.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(restclient :variables
|
||
restclient-use-org t)
|
||
#+END_SRC
|
||
|
||
LanguageTool works with Flyspell and will check for grammatical issues in my
|
||
english texts.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(languagetool :variables
|
||
langtool-default-language "en-US"
|
||
languagetool-show-error-on-jump t
|
||
langtool-java-classpath "/usr/share/languagetool:/usr/share/java/languagetool/*")
|
||
#+END_SRC
|
||
|
||
|
||
And finally, we also have the Shell layer for which I specified its default
|
||
height when spawning at the bottom of the screen should be 40 lines high, and
|
||
the default shell to invoke is Eshell.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(shell :variables
|
||
shell-default-height 40
|
||
shell-default-position 'bottom
|
||
shell-default-shell 'eshell)
|
||
#+END_SRC
|
||
|
||
*** Web Services
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Spacemacs_layers_and_packages-Layers-Web_Services-c2888251
|
||
:END:
|
||
In this category, I have only enabled a layer for Twitter support.
|
||
#+BEGIN_SRC emacs-lisp
|
||
twitter
|
||
#+END_SRC
|
||
|
||
*** Custom layers
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Spacemacs_layers_and_packages-Layers-Custom_layers-69473533
|
||
:END:
|
||
Lastly, one custom layers have been enabled: my custom layer for conlanging
|
||
tools.
|
||
#+BEGIN_SRC emacs-lisp
|
||
conlanging
|
||
#+END_SRC
|
||
|
||
# Don’t delete this code block, it wraps the layers
|
||
#+BEGIN_SRC emacs-lisp :exports none
|
||
))
|
||
#+END_SRC
|
||
|
||
* Init
|
||
:PROPERTIES:
|
||
:header-args:emacs-lisp: :mkdirp yes :tangle ~/.config/emacs/private/spacemacs-init.el :exports code :results silent :lexical t
|
||
:CUSTOM_ID: Init-99a4b561
|
||
:END:
|
||
#+BEGIN_SRC emacs-lisp :exports none
|
||
;; -*- lexical-binding: t -*-
|
||
#+END_SRC
|
||
|
||
The ~dotspacemacs/init~ function is the one called at the very begining of the
|
||
Spacemacs startup, before any kind of configuration, including the layer
|
||
configuration. Only the values of the Spacemacs settings should be modified
|
||
here. By default, every values are set in a ~setq-default~ sexp, and they
|
||
represent all the supported Spacemacs settings. Hence, the function looks like
|
||
this:
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
(defun dotspacemacs/init ()
|
||
(setq-default
|
||
;; default Spacemacs configuration here
|
||
))
|
||
#+END_SRC
|
||
|
||
** Handling my Spacemacs litterate config
|
||
:PROPERTIES:
|
||
:header-args:emacs-lisp: :tangle no :exports code :results silent :lexical t
|
||
:CUSTOM_ID: Init-Handling-my-Spacemacs-litterate-config-679170db
|
||
:END:
|
||
Just before we get onto the usual content of the ~dotspacemacs/init~ function
|
||
you would find in a typical Spacemacs installation, I would like to talk a bit
|
||
about how I manage writing a litterate config for Spacemacs and ensure Emacs
|
||
starts with an up-to-date configuration from said litterate config. For that, I
|
||
actually declared a couple of variables:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defvar phundrak--dotspacemacs-src-dir "~/.config/emacs/private/"
|
||
"Directory for my exported Elisp configuration files")
|
||
(defvar phundrak--dotspacemacs-src "~/org/config/emacs.org"
|
||
"My litterate config file for Emacs")
|
||
(defvar phundrak--dotspacemacs-si (concat phundrak--dotspacemacs-src-dir "spacemacs-init"))
|
||
(defvar phundrak--dotspacemacs-sl (concat phundrak--dotspacemacs-src-dir "spacemacs-layers"))
|
||
(defvar phundrak--dotspacemacs-uc (concat phundrak--dotspacemacs-src-dir "user-config"))
|
||
(defvar phundrak--dotspacemacs-ui (concat phundrak--dotspacemacs-src-dir "user-init"))
|
||
(defvar phundrak--dotspacemacs-files (list phundrak--dotspacemacs-si phundrak--dotspacemacs-sl
|
||
phundrak--dotspacemacs-uc phundrak--dotspacemacs-ui))
|
||
#+END_SRC
|
||
I also declared the following function that tells me if my Elisp files are more
|
||
recent than my ~emacs.org~ file. The ~compiled?~ argument lets me compare either
|
||
the ~.el~ files if it is ~nil~, or the ~.elc~ files if it is ~t~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun phundrak-update-config-files-p (&optional compiled?)
|
||
"Verify if any of my exported Elisp configuration files are
|
||
newer than my litterate configuration.
|
||
|
||
If `COMPILED?' is `t', check the `.elc' files instead of the
|
||
`.el' files."
|
||
(catch 'ret
|
||
(dolist (file phundrak--dotspacemacs-files)
|
||
(when (file-newer-than-file-p phundrak--dotspacemacs-src
|
||
(format "%s.%s"
|
||
file
|
||
(if compiled? "elc" "el")))
|
||
(throw 'ret t)))))
|
||
#+END_SRC
|
||
|
||
Now I know a couple of my files that get exported by this document. If I compare
|
||
how recent these files are compared to my litterate config, I know if Emacs
|
||
missed tangling its configuration before launching, so if any of my ~si~, ~sl~,
|
||
~uc~, or ~ui~ files are older than my ~emacs.org~, then I’ll tangle the latter;
|
||
and since my user config is growing longer and longer, I want Emacs to be able
|
||
to parse it fast next time it boots, so let’s compile my exported ~.el~ files!
|
||
#+BEGIN_SRC emacs-lisp
|
||
(when (or (file-newer-than-file-p phundrak--dotspacemacs-src (concat phundrak--dotspacemacs-si ".el"))
|
||
(file-newer-than-file-p phundrak--dotspacemacs-src (concat phundrak--dotspacemacs-sl ".el"))
|
||
(file-newer-than-file-p phundrak--dotspacemacs-src (concat phundrak--dotspacemacs-ui ".el"))
|
||
(file-newer-than-file-p phundrak--dotspacemacs-src (concat phundrak--dotspacemacs-uc ".el")))
|
||
(message "Exporting new Emacs configuration from spacemacs.org through org-babel...")
|
||
(with-temp-buffer
|
||
(shell-command (format "emacs -Q --batch %s %s %s"
|
||
"--eval \"(require 'ob-tangle)\""
|
||
"--eval \"(setq org-confirm-babel-evaluate nil)\""
|
||
(format "--eval '(org-babel-tangle-file \"%s\")'"
|
||
phundrak--dotspacemacs-src))
|
||
(current-buffer)))
|
||
(message "Exporting new Emacs configuration from spacemacs.org through org-babel...done")
|
||
(with-temp-buffer
|
||
(byte-recompile-directory phundrak--dotspacemacs-src-dir
|
||
0 t)))
|
||
#+END_SRC
|
||
All that’s left to do in the Spacemacs functions is to call ~load~ on ~si~,
|
||
~sl~, ~uc~, and ~ui~. Be aware this sub-chapter won’t be tangled, so it might
|
||
not be up to date with the [[https://labs.phundrak.com/phundrak/dotfiles/src/branch/master/.spacemacs][actual dotspacemacs file]]. Please check it just in
|
||
case something changed and I forgot to update this part of ~emacs.org~.
|
||
|
||
** Emacs with pdumper
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Init-Emacs_with_pdumper-f24ab30b
|
||
:END:
|
||
It is possible to compile Emacs 27 from source with support for the portable
|
||
dumper, as shown in Spacemacs’ ~EXPERIMENTAL.org~ file. I do not use this
|
||
feature yet, as I am still on Emacs 26 provided from Arch Linux’s repositories,
|
||
so I’ll disable the Spacemacs support for this feature. The default value of
|
||
this variable is ~nil~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-enable-emacs-pdumper t)
|
||
#+END_SRC
|
||
|
||
In case the support for pdumper was enabled, Spacemacs needs to know the name of
|
||
the Emacs executable which supports such a feature. The executable must be in
|
||
the user’s ~PATH~. By default, the value of the variable is ~"emacs"~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-emacs-pdumper-executable-file "emacs")
|
||
#+END_SRC
|
||
|
||
And finally, we can name the Spacemacs dump file. This is the file that will be
|
||
created by the portable dumper in the cache directory under the ~dumps~
|
||
sub-directory. To load it when starting Emacs, the parameter ~--dump-file~
|
||
should be added when invoking Emacs 27.1 executable from the command line, for
|
||
instance:
|
||
#+BEGIN_SRC sh :tangle no :exports code
|
||
./emacs --dump-file=~/.config/emacs/.cache/dumps/spacemacs.pdmp
|
||
#+END_SRC
|
||
|
||
The default value of this variable is ~"spacemacs.pdmp"~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-emacs-dumper-dump-file
|
||
(format "spacemacs-%s.pdmp" emacs-version))
|
||
#+END_SRC
|
||
|
||
** Package managment and updates
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Init-Package_managment_and_updates-79363da3
|
||
:END:
|
||
Spacemacs’ core configuration can be updated via git commands using Github
|
||
services. If Spacemacs is not set to the ~develop~ branch, it can check by
|
||
itself if any update is available. However, I am using said branch, therefore I
|
||
should set this variable to ~nil~. The default value is ~nil~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-check-for-update nil)
|
||
#+END_SRC
|
||
|
||
When it comes to package management, Spacemacs is able to store them in
|
||
different directories depending on the version of Emacs used or based on other
|
||
variables. I personally prefer to use the value ~emacs-version~ since it makes
|
||
it easier to upgrade or downgrade Emacs without any conflict with the already
|
||
installed packages. The default value is ~emacs-version~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-elpa-subdirectory 'emacs-version)
|
||
#+END_SRC
|
||
|
||
Spacemacs has a capacity of performing rollbacks after updates. We can set the
|
||
maximum number of rollback slots to keep in the cache. The default value is ~5~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-max-rollback-slots 5)
|
||
#+END_SRC
|
||
|
||
*** Elpa repository
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Init-Package_managment_and_updates-Elpa_repository-c07fb1c4
|
||
:END:
|
||
It is possible to ask Emacs to use an HTTPS connection when contacting the Elpa
|
||
whenever possible. This value should be set to ~nil~ when the user has no way to
|
||
contact the Elpa though HTTPS, otherwise it is strongly recommended to let it
|
||
set to ~t~. This variable however has no effect if Emacs is launched with the
|
||
parameter ~--insecure~ which forces the value of this variable to ~nil~. The
|
||
default value is ~t~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-elpa-https t)
|
||
#+END_SRC
|
||
|
||
We can set a maximum amount of seconds which will represent the maximum allowed
|
||
time to contact the Elpa repository. By default, this setting is set on ~5~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-elpa-timeout 5)
|
||
#+END_SRC
|
||
|
||
*** Spacelpa repository
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Init-Package_managment_and_updates-Spacelpa_repository-a431b288
|
||
:END:
|
||
The Spacelpa repository is a Spacemacs-specific package repository. It is
|
||
possible to use it as the primary source to install a locked version of a
|
||
package. If the below value is set to ~nil~, then Spacemacs will install the
|
||
latest version of packages from MELPA. I personally don’t use it, so I let it
|
||
set to ~nil~. The default value is ~nil~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-use-spacelpa nil)
|
||
#+END_SRC
|
||
|
||
If the below value is not ~nil~, then the signature for the downloaded Spacelpa
|
||
packages must be verified.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-verify-spacelpa-archives t)
|
||
#+END_SRC
|
||
|
||
** Editing style
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Init-Editing_style-56d58a4b
|
||
:END:
|
||
By default, Spacemacs encourages the use of evil-mode, which brings vim
|
||
keybinding in Emacs. Still, it has three different styles available:
|
||
- ~vim~, which goes full evil-mode usage and most adapted to Emacs newcomers,
|
||
especially if they were used to vim before, with the use of a normal mode and
|
||
an insert mode.
|
||
- ~emacs~ which keeps an Emacs-like feel to the keybindings, without any
|
||
difference between an insert or normal mode.
|
||
- ~hybrid~ is a modification of the ~vim~ mode which brings the ~emacs~ style in
|
||
insert mode, but otherwise behaves like the ~vim~ style in normal mode. This
|
||
is the style I personally use.
|
||
The value can also be a list with the ~:variables~ keyword (similar to layers).
|
||
Check the editing styles section of the documentation for details on available
|
||
variables. The default value is ~vim~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-editing-style
|
||
'(hybrid :variables
|
||
hybrid-mode-enable-evilified-state t
|
||
hybrid-mode-default-state 'normal))
|
||
#+END_SRC
|
||
|
||
If non-nil, the paste transient-state is enabled. While enabled, after you paste
|
||
something, pressing ~C-j~ and ~C-k~ several times cycles through the elements in
|
||
the ~kill-ring~. Default ~nil~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-enable-paste-transient-state t)
|
||
#+END_SRC
|
||
|
||
** Spacemacs home configuration
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Init-Spacemacs_home_configuration-8375cdcc
|
||
:END:
|
||
The value below specifies the startup banner of Spacemacs. The default value is
|
||
~official~, it displays the official Spacemacs logo. An integer value is the
|
||
index of text banner, ~random~ chooses a random text banner in the
|
||
~core/banners~ directory. A string value must be a path to an image format
|
||
supported by your Emacs build. If the value is nil, then no banner is displayed.
|
||
The default value is ~official~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-startup-banner 'official)
|
||
#+END_SRC
|
||
|
||
On the Spacemacs homepage, a list of elements can also be shown, be it recent
|
||
files, projects, agenda items,… Each of the elements making up the list value of
|
||
the below variable are pairs in the form ~(list-type . list-size)~. If the value
|
||
is ~nil~, then it is disabled. The possible values for ~list-type~ are:
|
||
- ~recents~ :: displays recently opened files
|
||
- ~bookmarks~ :: displays saved bookmarks
|
||
- ~projects~ :: displays projectile projects recently opened
|
||
- ~agenda~ :: displays upcoming events from Org-mode agendas
|
||
- ~todos~ :: displays recent TODOs detected in projectile projects
|
||
The order in which they are set in the below list affects their order on the
|
||
Spacemacs startup page. List sikes may be ~nil~, in which case
|
||
~spacemacs-buffer-startup-lists-length~ takes effect.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-startup-lists '((recents . 15)
|
||
(projects . 15)))
|
||
#+END_SRC
|
||
|
||
The below variable allows the startup page to respond to resize events. Its
|
||
default value is ~t~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-startup-buffer-responsive t)
|
||
#+END_SRC
|
||
|
||
If non-nil show the version string in the Spacemacs buffer. It will appear as
|
||
~(spacemacs version)@(emacs version)~. Default ~t~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-startup-buffer-show-version t)
|
||
#+END_SRC
|
||
|
||
** Default major modes
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Init-Default_major_modes-37f4a891
|
||
:END:
|
||
The below variable sets a default major mode for a new empty buffer. Possible
|
||
values are mode names such as ~text-mode~, or ~nil~ to use Fundamental mode. The
|
||
default value is ~text-mode~, but I prefer to use ~org-mode~ by default.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-new-empty-buffer-major-mode 'org-mode)
|
||
#+END_SRC
|
||
|
||
Similarly, the below variable sets the default mode for the scratch buffer. Its
|
||
default value is ~text-mode~, but I set it to use ~emacs-lisp-mode~ by default.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-scratch-mode 'emacs-lisp-mode)
|
||
#+END_SRC
|
||
|
||
By the way, it is possible to set a default message for the scratch buffer, such
|
||
as “Welcome to Spacemacs!”. I prefer to keep it clean. The default value is
|
||
~nil~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-initial-scratch-message nil)
|
||
#+END_SRC
|
||
|
||
** Visual configuration
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Init-Visual_configuration-c4116cc1
|
||
:END:
|
||
*** Themes
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Init-Visual_configuration-Themes-315992bb
|
||
:END:
|
||
Spacemacs makes it quite easy to use themes and organize them. The below value
|
||
is a list of themes, the first of the list is loaded when Spacemacs starts. The
|
||
user can press ~SPC T n~ to cycle to the next theme in the list.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-themes '(nord doom-nord doom-vibrant spacemacs-dark
|
||
doom-one doom-opera doom-dracula doom-molokai
|
||
doom-peacock doom-sourcerer doom-spacegrey
|
||
kaolin-dark kaolin-aurora kaolin-bubblegum
|
||
kaolin-galaxy kaolin-mono-dark kaolin-temple
|
||
kaolin-valley-dark))
|
||
#+END_SRC
|
||
|
||
Emacs also makes use of themes for the Spaceline at the bottom of buffers.
|
||
Supported themes are:
|
||
- ~spacemacs~
|
||
- ~all-the-icons~
|
||
- ~custom~
|
||
- ~doom~ (the one I use)
|
||
- ~vim-powerline~
|
||
- ~vanilla~
|
||
The first three are Spaceline themes. ~doom~ is the Doom-Emacs mode-line, and
|
||
~vanilla~ is the default Emacs mode-line. ~custom~ is a user defined theme,
|
||
refer to Spacemacs’ ~DOCUMENTATION.org~ file for more info on how to create your
|
||
own Spaceline theme. Value can be a symbol or list with additional properties.
|
||
The default value is ~'(spacemacs :separator wave :separator-scale 1.5))~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-mode-line-theme '(doom
|
||
:separator wave
|
||
:separator-scale 1.0))
|
||
#+END_SRC
|
||
|
||
It is also possible to color the cursor depending on which mode Spacemacs is in,
|
||
in order to mach the state color in GUI Emacs. The default value is ~t~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-colorize-cursor-according-to-state t)
|
||
#+END_SRC
|
||
|
||
The below variable sets either the default font or a prioritized list of fonts
|
||
to be used by Emacs. The ~:size~ can be specified as a non-negative integer
|
||
(pixel size), or a floating-point (point size). Point size is recommended,
|
||
because it’s device independent (add a ~.0~ to make an integer a floating
|
||
point). The default size is ~10.0~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-default-font '("Cascadia Code"
|
||
:size 9.0))
|
||
#+END_SRC
|
||
|
||
I also added the following code in order to define a fallback font for emojis,
|
||
defined only on their unicode range:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(set-fontset-font "fontset-default" '(#x1f600 . #x1f64f) "NotoEmoji Nerd Font")
|
||
#+END_SRC
|
||
|
||
*** Other on-screen elements
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Init-Visual_configuration-Other_on-screen_elements-7d334e09
|
||
:END:
|
||
~which-key~ is a helper which displays available keyboard shortcuts. This
|
||
variable sets in seconds the time Spacemacs should wait between a key press and
|
||
the moment ~which-key~ should be shown.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-which-key-delay 1)
|
||
#+END_SRC
|
||
|
||
This variable sets ~which-key~'s frame position. Possible values are:
|
||
- ~right~
|
||
- ~bottom~
|
||
- ~right-then-bottom~
|
||
~right-then-bottom~ tries to display the frame to the right, but if there is
|
||
insufficient space it displays it at the bottom. The default value is ~bottom~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-which-key-position 'right-then-bottom)
|
||
#+END_SRC
|
||
|
||
This controls where ~switch-to-buffer~ displays the buffer. If the value is
|
||
~nil~, ~switch-to-buffer~ displays the buffer in the current window even if
|
||
another same-purpose window is available. If non-nil, ~switch-to-buffer~
|
||
displays the buffer in a same-purpose window even if the buffer can be displayed
|
||
in the current window. The default value is ~nil~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-switch-to-buffer-prefers-purpose nil)
|
||
#+END_SRC
|
||
|
||
If this variable is non-nil, a progress bar is displayed when Spacemacs is
|
||
loading. This may increase the boot time on some systems and emacs builds, set
|
||
it to ~nil~ to boost the loading time. The default value is ~t~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-loading-progress-bar t)
|
||
#+END_SRC
|
||
|
||
If the value is non-nil, Emacs will show the title of the transient states. The
|
||
default value is ~t~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-show-transient-state-title t)
|
||
#+END_SRC
|
||
|
||
If non-nil, this will show the color guide hint for transient state keys. The
|
||
default value is ~t~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-show-transient-state-color-guide t)
|
||
#+END_SRC
|
||
|
||
If non-nil, unicode symbols are displayed in the mode line. If you use Emacs as
|
||
a daemon and want unicode characters only in GUI set the value to quoted
|
||
~display-graphic-p~. The default value is ~t~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-mode-line-unicode-symbols t)
|
||
#+END_SRC
|
||
|
||
If non-nil, smooth scrolling (native-scrolling) is enabled. Smooth scrolling
|
||
overrides the default behavior of Emacs which recenters point when it reaches
|
||
the top or bottom of the screen. The default value is ~t~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-smooth-scrolling t)
|
||
#+END_SRC
|
||
|
||
The following value controls the line number activation. If set to ~t~,
|
||
~relative~ or ~visual~ then line numbers are enabled in all ~prog-mode~ and
|
||
~text-mode~ derivatives. If set to ~relative~, line numbers are relative. If set
|
||
to ~visual~, line numbers are also relative, but only visual lines are counted.
|
||
For example, folded lines will not be counted and wrapped lines are counted as
|
||
multiple lines. This variable can also be set to a property list for finer
|
||
control:
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
'(:relative nil
|
||
:visual nil
|
||
:disabled-for-modes dired-mode
|
||
doc-view-mode
|
||
markdown-mode
|
||
org-mode
|
||
pdf-view-mode
|
||
text-mode
|
||
:size-limit-kb 1000)
|
||
#+END_SRC
|
||
|
||
When used in a plist, ~visual~ takes precendence over ~relative~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-line-numbers '(:relative nil
|
||
:visual nil
|
||
:disabled-for-modes org-mode pdf-view-mode
|
||
dired-mode doc-view-mode
|
||
text-mode))
|
||
#+END_SRC
|
||
|
||
Select a scope to highlight delimiter. Possible values are:
|
||
- ~any~
|
||
- ~current~
|
||
- ~all~
|
||
- ~nil~
|
||
The default value is ~all~ (highlights any scope and emphasis the current one).
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-highlight-delimiters 'all)
|
||
#+END_SRC
|
||
|
||
After a certain amount of time in seconds, Spacemacs can zone-out. The default
|
||
value is ~nil~. I set it so Spacemacs zones out after 15 minutes.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-zone-out-when-idle 900)
|
||
#+END_SRC
|
||
|
||
Run ~spacemacs/prettify-org-buffer~ when visiting the ~README.org~ files of
|
||
Spacemacs. The default value is ~nil~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-pretty-docs nil)
|
||
#+END_SRC
|
||
|
||
If ~nil~, the home buffer shows the full path of agenda items and todos. If non
|
||
~nil~, only the file name is shown.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-home-shorten-agenda-source t)
|
||
#+END_SRC
|
||
|
||
*** Appearance of Emacs frames
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Init-Visual_configuration-Appearance_of_Emacs_frames-59700bb7
|
||
:END:
|
||
Starting from Emacs 24.4, it is possible to make the Emacs frame fullscreen when
|
||
Emacs starts up if the variable is set to a non-nil value. The default value is
|
||
~nil~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-fullscreen-at-startup nil)
|
||
#+END_SRC
|
||
|
||
This variable is to be used if the user does not want to use native fullscreen
|
||
with ~spacemacs/toggle-fullscreen~. This disables for instance the fullscreen
|
||
animation under OSX. The default value is ~nil~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-fullscreen-use-non-native nil)
|
||
#+END_SRC
|
||
|
||
If you do not start Emacs in fullscreen at startup, you might want it to be
|
||
maximized by default. If the value for the variable below is set to be non-nil,
|
||
the frame will be maximized. This can only work if
|
||
~dotspacemacs-fullscreen-at-startup~ is set to ~nil~, and it is only available
|
||
from Emacs 24.4 onwards. The default value is ~nil~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-maximized-at-startup nil)
|
||
#+END_SRC
|
||
|
||
If non-nil, the frame is undecorated when Emacs starts up. Combine this with the
|
||
variable ~dotspacemacs-maximized-at-startup~ in OSX to obtain borderless
|
||
fullscreen. The default value is ~nil~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-undecorated-at-startup nil)
|
||
#+END_SRC
|
||
|
||
You can also set a transparency level for Emacs when you toggle the transparency
|
||
of the frame with ~toggle-transparency~. The value of the transparency, going
|
||
from 0 to 100 in increasing opacity, describes the transparency level of a frame
|
||
when it’s active or selected. The default value is ~90~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-active-transparency 85)
|
||
#+END_SRC
|
||
|
||
Similarly, you can set a value from 0 to 100 in increasing opacity which
|
||
describes the transparency level of a frame when it’s inactive or deselected.
|
||
The default value is ~90~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-inactive-transparency 80)
|
||
#+END_SRC
|
||
|
||
The variable below sets the format of frame title. You can use:
|
||
- ~%a~ :: the ~abbreviated-file-name~ or ~buffer-name~
|
||
- ~%t~ :: ~projectile-project-name~
|
||
- ~%I~ :: ~invocation-name~
|
||
- ~%S~ :: ~system-name~
|
||
- ~%U~ :: contents of ~$USER~
|
||
- ~%b~ :: buffer name
|
||
- ~%f~ :: visited file name
|
||
- ~%F~ :: frame name
|
||
- ~%s~ :: process status
|
||
- ~%p~ :: percent of buffer above top of window, or Top, Bot, or All
|
||
- ~%P~ :: percent of buffer above bottom of window, perhaps plus Top, or Bot, or
|
||
All
|
||
- ~%m~ :: mode name
|
||
- ~%n~ :: Narrow if appropriate
|
||
- ~%z~ :: mnemonics of buffer, terminal, and keyboard coding systems
|
||
- ~%Z~ :: like ~%z~, but including the end-of-line format
|
||
The default value is ~"%I@%S"~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-frame-title-format "Emacs: %b (%t) %U@%S")
|
||
#+END_SRC
|
||
|
||
Format specification for setting the icon title format. The default value is
|
||
~nil~, same as ~frame-title-format~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-icon-title-format nil)
|
||
#+END_SRC
|
||
|
||
** Spacemacs leader keys and shortcuts
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Init-Spacemacs_leader_keys_and_shortcuts-ebf21abe
|
||
:END:
|
||
The below setting sets the Spacemacs leader key. By default, this is the ~SPC~
|
||
key.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-leader-key "SPC")
|
||
#+END_SRC
|
||
|
||
Once the leader key has been pressed, it is possible to set another key in order
|
||
to call Emacs’ command ~M-x~. By default, it is again the ~SPC~ key.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-emacs-command-key "SPC")
|
||
#+END_SRC
|
||
|
||
It is also possible to invoke Vim Ex commands with the press of a key, and by
|
||
default it is the ~:~ key.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-ex-command-key ":")
|
||
#+END_SRC
|
||
|
||
The below variable sets the leader key accessible in ~emacs-state~ and
|
||
~insert-state~:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-emacs-leader-key "M-m")
|
||
#+END_SRC
|
||
|
||
The major mode leader key is a shortcut key which is the equivalent of pressing
|
||
~<leader> m~. Set it to ~nil~ to disable it. Its default value is ~,~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-major-mode-leader-key ",")
|
||
#+END_SRC
|
||
|
||
In ~emacs-state~ and ~insert-state~, the same major mode leader key can be
|
||
accessible from another shortcut, which by default is ~C-M-m~ in terminal mode,
|
||
or ~M-return~ in GUI mode.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-major-mode-emacs-leader-key
|
||
(if window-system "<M-return>" "C-M-m"))
|
||
#+END_SRC
|
||
|
||
These variables control whether separate commands are bound in the GUI to the
|
||
key pairs ~C-i~ and ~TAB~, and ~C-m~ and ~RET~. Setting it to a non-nil value
|
||
allows for separate commands under ~C-i~ and ~TAB~, and ~C-m~ and ~RET~. In the
|
||
terminal, these pairs are generally indistinguishable, so this only works in the
|
||
GUI. The default value is ~nil~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-distinguish-gui-tab nil)
|
||
#+END_SRC
|
||
|
||
** Layouts
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Init-Layouts-61c0374d
|
||
:END:
|
||
The variable belows sets the name of the default layout. Its default value is
|
||
~"Default"~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-default-layout-name "Default")
|
||
#+END_SRC
|
||
|
||
If non-nil, the default layout name is displayed in the mode-line. The default
|
||
value is ~nil~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-display-default-layout nil)
|
||
#+END_SRC
|
||
|
||
If non-nil, then the last auto saved layouts are resumed automatically upon
|
||
start. The default value is ~nil~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-auto-resume-layouts nil)
|
||
#+END_SRC
|
||
|
||
If non-nil, the layout name will be auto-generated when creating new layouts. It
|
||
only has an effect when using the “jump to layout by number” command. The
|
||
default value is ~nil~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-auto-generate-layout-names nil)
|
||
#+END_SRC
|
||
|
||
** Files-related settings
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Init-Files-related_settings-67fba383
|
||
:END:
|
||
The below value sets the size in MB above which Spacemacs will prompt to open
|
||
the file literally in order to avoid preformance issues. Opening a file
|
||
literally means that no major or minor mode is active. The default value is ~1~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-large-file-size 5)
|
||
#+END_SRC
|
||
|
||
This variable sets the location where to auto-save files. Possible values are:
|
||
- ~original~ :: auto-saves files in-place
|
||
- ~cache~ :: auto-saves files in another file stored in the cache directory
|
||
- ~nil~ :: disables auto-saving.
|
||
The default value is ~cache~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-auto-save-file-location 'original)
|
||
#+END_SRC
|
||
|
||
** Emacs server
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Init-Emacs_server-d3947c28
|
||
:END:
|
||
Emacs can be launched as a server if the following value is set to non-nil and
|
||
if one isn’t already running. The default value is ~nil~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-enable-server nil)
|
||
#+END_SRC
|
||
|
||
You can also set a custom emacs server socket location. If the value is ~nil~,
|
||
Emacs will use whatever the Emacs default is, otherwise a directory path like
|
||
="$HOME/.config/emacs/server"=. It has no effect if ~dotspacemacs-enable-server~
|
||
is ~nil~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-server-socket-dir nil)
|
||
#+END_SRC
|
||
|
||
It is also possible to tell Emacs that the quit function should keep the server
|
||
open when quitting. The default value is ~nil~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-persistent-server t)
|
||
#+END_SRC
|
||
|
||
** Miscellaneous
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Init-Miscellaneous-6b4f0b76
|
||
:END:
|
||
This value changes the folding method of code blocks. The possible values are
|
||
either ~evil~, the default value, or ~origami~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-folding-method 'evil)
|
||
#+END_SRC
|
||
|
||
If non-nil, ~smartparens-strict-mode~ will be enabled in programming modes. The
|
||
default value is ~nil~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-smartparens-strict-mode nil)
|
||
#+END_SRC
|
||
|
||
If non-nil, pressing the closing parenthesis ~)~ key in insert mode passes over
|
||
any automatically added closing parenthesis, bracket, quote, etc… This can
|
||
temporarily disabled by pressing ~C-q~ before ~)~. The default value is ~nil~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-smart-closing-parenthesis nil)
|
||
#+END_SRC
|
||
|
||
List of search tool executable names. Spacemacs uses the first installed tool of
|
||
the list. Supported tools are:
|
||
- ~rg~
|
||
- ~ag~
|
||
- ~pt~
|
||
- ~ack~
|
||
- ~grep~
|
||
The default value is ~'("rg" "ag" "pt" "ack" "grep")~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-search-tools '("rg" "grep"))
|
||
#+END_SRC
|
||
|
||
Delete whitespace while saving buffer. Possible values are:
|
||
- ~all~ :: aggresively delete empty lines and long sequences of whitespace
|
||
- ~trailing~ :: only detele the whitespace at end of lines
|
||
- ~changed~ :: to delete only whitespace for changed lines
|
||
- ~nil~ :: disable cleanup
|
||
The default value is ~nil~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-whitespace-cleanup nil)
|
||
#+END_SRC
|
||
|
||
Set ~gc-cons-threshold~ and ~gc-cons-percentage~ when startup finishes. This is
|
||
an advanced option and should not be changed unless you suspect performance
|
||
issues due to garbage collection operations. The default is ~'(100000000 0.1)~
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-gc-cons '(100000000 0.1))
|
||
#+END_SRC
|
||
|
||
If non nil activate ~clean-aindent-mode~ which tries to correct virtual
|
||
indentation of simple modes. This can interfer with mode specific indent
|
||
handling like has been reported for ~go-mode~. If it does deactivate it here.
|
||
Default ~t~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-use-clean-aindent-mode t)
|
||
#+END_SRC
|
||
|
||
If non ~nil~, shift your number row to match the entered keyboard layout (only
|
||
in insert state). Currently supported keyboard layouts are ~querty-us~,
|
||
~quertz-de~ and ~querty-ca-fr~. New layouts can be added in ~spacemacs-editing~
|
||
layer. Default ~nil~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-swap-number-row nil)
|
||
#+END_SRC
|
||
|
||
Set ~read-process-output-max~ when startup finishes. This defines how much data
|
||
is read from a foreign process. Setting this >= 1 MB should increase performance
|
||
for lsp servers in emacs 27.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq-default dotspacemacs-read-process-output-max (* 1024 1024 8))
|
||
#+END_SRC
|
||
|
||
* User Initialization
|
||
:PROPERTIES:
|
||
:header-args:emacs-lisp: :mkdirp yes :tangle ~/.config/emacs/private/user-init.el :exports code :results silent :lexical t
|
||
:CUSTOM_ID: User_Initialization-e0d21089
|
||
:END:
|
||
#+BEGIN_SRC emacs-lisp :exports none
|
||
;; -*- lexical-binding: t -*-
|
||
#+END_SRC
|
||
|
||
While Emacs and especially Spacemacs loads, I want it to initialize some
|
||
elements and load some packages. First of all, I want it to load my private
|
||
Emacs config file:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(load "~/.config/emacs/private/private_emacs")
|
||
#+END_SRC
|
||
|
||
Then, I want a couple of requires:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(require 'org-id)
|
||
(require 'org-protocol)
|
||
(require 'package)
|
||
(require 'ox-latex)
|
||
(require 'ox-publish)
|
||
(require 'tramp)
|
||
#+END_SRC
|
||
|
||
I would also like to enable the setup of flycheck for Rust when Flycheck is
|
||
loaded:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(add-hook 'flycheck-mode-hook #'flycheck-rust-setup)
|
||
#+END_SRC
|
||
|
||
By default, Flyspell should be disabled and only enabled manually.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(flyspell-mode 0)
|
||
#+END_SRC
|
||
|
||
Finally, here is a quick workaround for Tramp, sometimes it cannot connect to my
|
||
hosts if I don’t have this code snippet.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq tramp-ssh-controlmaster-options
|
||
"-o ControlMaster=auto -o ControlPath='tramp.%%C' -o ControlPersist=no")
|
||
#+END_SRC
|
||
|
||
* User Configuration
|
||
:PROPERTIES:
|
||
:header-args:emacs-lisp: :mkdirp yes :tangle ~/.config/emacs/private/user-config.el :exports code :results silent :lexical t
|
||
:CUSTOM_ID: User_Configuration-4a937fe5
|
||
:END:
|
||
#+BEGIN_SRC emacs-lisp :exports none
|
||
;; -*- lexical-binding: t -*-
|
||
#+END_SRC
|
||
** Custom functions, macros, and variables
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Custom_functions-ceb4bc42
|
||
:END:
|
||
In this section, I will put my various custom functions that do not fit in other
|
||
sections and which are more oriented towards general usage throughout Emacs and
|
||
in Elisp code.
|
||
|
||
Almost all of my code snippets will be prefixed by either my name or the name of
|
||
the package or layer they are part of, unless they are an explicit overwrite of
|
||
a function that already exists.
|
||
*** Eshell Prompt-Related Functions
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Custom-functions-macros-and-variables-Eshell-prompt-related-functions-79d07f21
|
||
:END:
|
||
**** ~phundrak-eshell-git-status~
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Custom-functions-macros-and-variables-phundrak-eshell-git-status-28f16e94
|
||
:END:
|
||
This function is used in my Eshell prompt which you can consult [[#User_Configuration-Eshell-Eshell_theme-a06715a9][here]]. This
|
||
function basically executes two git calls to get some information about a git
|
||
repo, which path we provide as an argument. Based on the result of these git
|
||
calls, the function will know what it needs to know about the repo to build a
|
||
git prompt that will be inserted in my Eshell prompt. And just for shit and
|
||
giggles, I’ve made it so it is a powerline prompt.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun phundrak-eshell-git-status ($path &optional $background-color)
|
||
"Returns a string indicating the status of the repository located
|
||
in `$PATH' if it exists. It should also append the name of the
|
||
current branch if it is not `master' or `main'.
|
||
`$BACKGROUND-COLOR' allows to choose the color that will be
|
||
visible behind the powerline characters. The theme is inspired by
|
||
the bobthefish theme for the fish shell which you can find here:
|
||
https://github.com/oh-my-fish/theme-bobthefish
|
||
|
||
Color code:
|
||
- green:
|
||
- orange: tracked stuff is staged but not commited
|
||
- red: tracked stuff is modified and not commited
|
||
|
||
Symbols:
|
||
- `*': dirty working dir, RED
|
||
- `~': staged changes, ORANGE
|
||
- `…': untracked files, GREEN
|
||
- `$': stashed changes
|
||
- `-': unpulled commits
|
||
- `-': unpushed commits
|
||
- `±': unpulled and unpushed commits"
|
||
(let* ((git-status-command (concat "cd " $path "; git status"))
|
||
(git-stash-status-command (concat "cd " $path "; git stash list"))
|
||
(status (eshell-command-result git-status-command))
|
||
(stashstat (eshell-command-result git-stash-status-command))
|
||
(detached (s-contains? "HEAD detached" status))
|
||
(dirty (s-contains? "Changes not staged for commit" status))
|
||
(staged (s-contains? "Changes to be committed" status))
|
||
(untracked (s-contains? "Untracked files" status))
|
||
(pullable (s-contains? "git pull" status))
|
||
(pushable (s-contains? "git push" status))
|
||
(branch (replace-regexp-in-string "On Branch \\(.*\\)\n\\(.\\|\n\\)*" "\\1" status))
|
||
(branch (unless (or (string= "master" branch)
|
||
(string= "main" branch)
|
||
detached)
|
||
branch)))
|
||
(let ((prompt (concat " "
|
||
(if detached ">" "")
|
||
(when branch (concat " " branch " "))
|
||
(when dirty "*")
|
||
(when staged "~")
|
||
(when untracked "…")
|
||
(cond ((and pullable pushable) "±")
|
||
(pullable "-")
|
||
(pushable "+"))
|
||
(when stashstat "$")
|
||
" "))
|
||
(accent (cond
|
||
(dirty phundrak-nord11)
|
||
(staged phundrak-nord13)
|
||
(t phundrak-nord14)))
|
||
(background (phundrak-var-or-if-nil $background-color
|
||
phundrak-nord0)))
|
||
(concat (with-face ""
|
||
:background accent
|
||
:foreground background)
|
||
(with-face prompt
|
||
:background accent
|
||
:foreground (if dirty phundrak-nord6 background))
|
||
(with-face ""
|
||
:background background
|
||
:foreground accent)))))
|
||
#+END_SRC
|
||
|
||
**** ~phundrak-git-repo-root~
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Custom-functions-macros-and-variables-phundrak-git-repo-root-f7cf3bb9
|
||
:END:
|
||
This function detects if the path passed as an argument points to a git
|
||
directory or to one of its subdirectories. If it is, it will return the path to
|
||
the root of the git repository, else it will return ~nil~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun phundrak-git-repo-root ($path)
|
||
"Return `$PATH' if it points to a git repository or one of its
|
||
subdirectories."
|
||
(when $path
|
||
(if (f-dir? (concat $path "/.git"))
|
||
$path
|
||
(phundrak-git-repo-root (f-parent $path)))))
|
||
#+END_SRC
|
||
|
||
**** ~phundrak-prompt-toggle-abbreviation~
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Custom-functions-macros-and-variables-phundrak-prompt-toggle-abbreviation-753ca549
|
||
:END:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defvar phundrak-prompt--abbreviate t
|
||
"Whether or not to abbreviate the displayed path in the Eshell
|
||
prompt.")
|
||
|
||
(defun phundrak-prompt-toggle-abbreviation ()
|
||
"Toggles whether the Eshell prompt should shorten the name of
|
||
the parent directories or not. See `phundrak-eshell-prompt'."
|
||
(interactive)
|
||
(setq phundrak-prompt--abbreviate (not phundrak-prompt--abbreviate)))
|
||
#+END_SRC
|
||
|
||
**** ~phundrak-abbr-path~
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Custom-functions-macros-and-variables-phundrak-abbr-path-559b46e3
|
||
:END:
|
||
The following is a nice little function I use in my Eshell prompt. It shortens
|
||
the name of all the parent directories of the current one in its path, but
|
||
leaves the current one written in full. It also abbreviates the equivalent of
|
||
the ~$HOME~ (~/home/<username>/~) directory to a simple =~=.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun phundrak-abbr-path ($path &optional $abbreviate)
|
||
"Abbreviate `$PATH'. If `$ABBREVIATE' is t, then all parent
|
||
directories of the current directory will be abbreviated to one
|
||
letter only. If a parent directory is a hidden directory (i.e.
|
||
preceeded by a dot), the directory will be abbreviated to the dot
|
||
plus the first letter of the name of the directory (e.g.
|
||
\".config\" -> \".c\").
|
||
|
||
For public use of the function, `$PATH' should be a string
|
||
representing a UNIX path. For internal use, `$PATH' can also be a
|
||
list. If `$PATH' is neither of those, an error will be thrown by
|
||
the function."
|
||
(cond
|
||
((stringp $path) (f-short
|
||
(if $abbreviate
|
||
(phundrak-abbr-path (f-split (phundrak-abbr-path $path)))
|
||
$path)))
|
||
((null $path) "")
|
||
((listp $path)
|
||
(f-join (cond ((= 1 (length $path)) (car $path))
|
||
(t (let* ((dir (car $path))
|
||
(first-char (s-left 1 dir)))
|
||
(if (string= "." first-char)
|
||
(s-left 2 dir)
|
||
first-char))))
|
||
(phundrak-abbr-path (cdr $path))))
|
||
(t (error "Invalid argument %s, neither stringp nor listp" $path))))
|
||
#+END_SRC
|
||
|
||
*** Files-Related Functions
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Custom-functions-macros-and-variables-Files-related-functions-0b66f353
|
||
:END:
|
||
**** ~phundrak-file-to-string~
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Custom-functions-macros-and-variables-phundrak-file-to-string-efab0fba
|
||
:END:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun phundrak-file-to-string (FILE)
|
||
"Returns the content of `FILE' as a string."
|
||
(with-temp-buffer
|
||
(insert-file-contents FILE)
|
||
(buffer-string)))
|
||
#+END_SRC
|
||
|
||
**** ~phundrak-find-org-files~
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Custom-functions-phundrak-find-org-files-a8fd200f
|
||
:END:
|
||
There are lots of files which I want to be able to quickly open. I used to have
|
||
one shortcut for each one of these files, but as their number grew, I decided to
|
||
switch to helm for my file selector which will be called by only one common
|
||
shortcut. Most of my files will be located in =~/org=, but I have some
|
||
conlanging files which are located in =~/Documents/conlanging=, and all my
|
||
university notes are in =~/Documents/university=. Let’s declare these
|
||
directories in a variable:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defvar phundrak-org-directories '("~/org"
|
||
"~/Documents/university/S8"
|
||
"~/Documents/conlanging")
|
||
"Directories in which to look for org files with the function
|
||
`phundrak-find-org-files'.")
|
||
#+END_SRC
|
||
|
||
With this established, let’s write some emacs-lisp that will allow me to get a
|
||
list of all these files and select them through helm. Be aware that I will be
|
||
using some functions from third party packages, such as [[https://github.com/magnars/s.el][s.el]] and [[https://github.com/magnars/dash.el][dash]], as well
|
||
as [[https://github.com/sharkdp/fd][fd]].
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun phundrak-find-org-files ()
|
||
"Find all org files in the directories listed in
|
||
`phundrak-org-directories', then list them in an ido buffer where
|
||
the user can match one and open it."
|
||
(interactive)
|
||
(find-file
|
||
(ido-completing-read
|
||
"Org File:"
|
||
(s-split "\n"
|
||
(mapconcat (lambda (path)
|
||
(shell-command-to-string
|
||
(format "fd . %s -e org -c never" path)))
|
||
phundrak-org-directories
|
||
"\n")))))
|
||
#+END_SRC
|
||
|
||
**** ~phundrak-open-marked-files~
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Custom-functions-macros-and-variables-phundrak-open-marked-files-b87d37f7
|
||
:END:
|
||
This function is particularly useful in Dired buffers when someone wants to open
|
||
multiple files. This function will basically look for all marked files in the
|
||
current dired buffer and open each one of them in their individual buffer.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun phundrak-open-marked-files ()
|
||
"This function allows the user to open all marked files in a
|
||
Dired buffer at once."
|
||
(interactive)
|
||
(let ((file-list (if (string= major-mode "dired-mode")
|
||
(dired-get-marked-files)
|
||
(list (buffer-file-name)))))
|
||
(mapc (lambda (file)
|
||
(find-file file))
|
||
file-list)))
|
||
#+END_SRC
|
||
|
||
**** ~xah/open-in-external-app~
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Custom-functions-macros-and-variables-xah-open-in-external-app-2655f31c
|
||
:END:
|
||
Here is another of Xah’s functions, this time to open a file externally to
|
||
Emacs. For instance, I sometimes want to open a PDF in Zathura rather than in
|
||
Emacs, or an HTML file in Firefox. With this function, it is now possible!
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun xah/open-in-external-app (&optional files)
|
||
"Open the current file or dired marked files in external app.
|
||
The app is chosen from your OS’ preference.
|
||
|
||
When called in emacs lisp, if `FILES' is given, open that.
|
||
|
||
URL `http://ergoemacs.org/emacs/emacs_dired_open_file_in_ext_apps.html'
|
||
Version 2019-01-18"
|
||
(interactive)
|
||
(let* (($file-list (if files
|
||
(progn (list files))
|
||
(if (string-equal major-mode "dired-mode")
|
||
(dired-get-marked-files)
|
||
(list (buffer-file-name)))))
|
||
($do-it-p (if (<= (length $file-list) 5)
|
||
t
|
||
(y-or-n-p "Open more than 5 files? "))))
|
||
(when $do-it-p
|
||
(mapc (lambda ($fpath)
|
||
(let ((process-connection-type nil))
|
||
(start-process "" nil "xdg-open" $fpath)))
|
||
$file-list))))
|
||
#+END_SRC
|
||
|
||
*** Theming
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Custom-functions-macros-and-variables-Theming-7ad4dc46
|
||
:END:
|
||
**** Nord theming variables
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Custom_functions_and_variables-Some_theming_variables-9b853a99
|
||
:END:
|
||
Yes, I do use a preconfigured theme, as mentioned above, but for some elements
|
||
such as Eshell, I need to define some variables for color, and I’ll do it here.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defvar phundrak-nord0 "#2e3440")
|
||
(defvar phundrak-nord1 "#3b4252")
|
||
(defvar phundrak-nord2 "#434c5e")
|
||
(defvar phundrak-nord3 "#4c566a")
|
||
(defvar phundrak-nord4 "#d8dee9")
|
||
(defvar phundrak-nord5 "#e5e9f0")
|
||
(defvar phundrak-nord6 "#eceff4")
|
||
(defvar phundrak-nord7 "#8fbcbb")
|
||
(defvar phundrak-nord8 "#88c0d0")
|
||
(defvar phundrak-nord9 "#81a1c1")
|
||
(defvar phundrak-nord10 "#5e81ac")
|
||
(defvar phundrak-nord11 "#bf616a")
|
||
(defvar phundrak-nord12 "#d08770")
|
||
(defvar phundrak-nord13 "#ebcb8b")
|
||
(defvar phundrak-nord14 "#a3be8c")
|
||
(defvar phundrak-nord15 "#b48ead")
|
||
#+END_SRC
|
||
|
||
**** ~with-face~
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Custom-functions-macros-and-variables-with-face-7974e15f
|
||
:END:
|
||
~with-face~ is a simple yet very useful macro that allows me to easily create
|
||
strings with faces defined as properties to the string passed as the first
|
||
argument. Here is how it is implemented:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defmacro with-face ($str &rest $properties)
|
||
"Helper macro for creating strings `$STR' with `$PROPERTIES'"
|
||
`(propertize ,$str 'face (list ,@$properties)))
|
||
#+END_SRC
|
||
|
||
*** Elisp Utilities and Predicates
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Custom-functions-macros-and-variables-Predicates-5598df46
|
||
:END:
|
||
**** ~phundrak-filter~
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Custom-functions-macros-and-variables-Elisp-Utilities-and-Predicates-phundrak-filter-2d3c5a5b
|
||
:END:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun phundrak-filter (fn list)
|
||
"Filter `LIST' according to the predicate `FN'.
|
||
|
||
All elements from `LIST' that do not satisfy the predicate `FN'
|
||
will be left out of the result, while all elements that do
|
||
satisfy it will be included in the resulting list. This function
|
||
also preserves the relative position between elements that
|
||
satisfy the predicate."
|
||
(declare (pure t) (side-effect-free t))
|
||
(when list
|
||
(let ((rest (phundrak-filter fn
|
||
(cdr list))))
|
||
(if (funcall fn
|
||
(car list))
|
||
(cons (car list) rest)
|
||
rest))))
|
||
#+END_SRC
|
||
|
||
**** ~phundrak-all?~
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Custom-functions-macros-and-variables-phundrak-all-0655600c
|
||
:END:
|
||
This function is inspired by dash’s ~-all?~ function: it will test all the
|
||
elements of the list ~seq~ against the predicate ~fn~ which should return either
|
||
~t~ or ~nil~. If all of them return something else than ~nil~, then it is a
|
||
success, otherwise it is a failure. Note that empty lists will always return
|
||
~t~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun phundrak-all? (fn seq)
|
||
"Check if all members of `SEQ' satisfy predicate `FN'. Note that
|
||
it will return t if `SEQ' is nil."
|
||
(declare (pure t) (side-effect-free t))
|
||
(if seq
|
||
(and (funcall fn (car seq))
|
||
(phundrak-all? fn (cdr seq)))
|
||
t))
|
||
#+END_SRC
|
||
|
||
**** ~phundrak-none?~
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Custom-functions-macros-and-variables-phundrak-none-463dee26
|
||
:END:
|
||
In the same vein as ~phundrak-all?~, ~phundrak-none?~ checks if all elements of
|
||
~seq~ do not satify the predicate ~fn~. Again, if the list is empty, it will
|
||
return ~t~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun phundrak-none? (fn seq)
|
||
"Check if all members of `SEQ' do not satisfy predicate `FN'.
|
||
Note that it will return t if `SEQ' is nil."
|
||
(declare (pure t) (side-effect-free t))
|
||
(if seq
|
||
(and (not (funcall fn (car seq)))
|
||
(phundrak-none? fn (cdr seq)))
|
||
t))
|
||
#+END_SRC
|
||
|
||
**** ~phundrak-var-or-if-nil~
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Custom-functions-macros-and-variables-phundrak-var-or-if-nil-40e2e54a
|
||
:END:
|
||
This simple function helps me return either the value ~var~ holds, or if it is
|
||
~nil~ it will return the value ~value~ holds (or will return).
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defmacro phundrak-var-or-if-nil (var value)
|
||
"Return the result yield by `VALUE' if `VAR' is nil, return `VAR' otherwise."
|
||
(if (null var)
|
||
value
|
||
var))
|
||
#+END_SRC
|
||
|
||
**** ~phundrak-zip~
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Custom-functions-macros-and-variables-Elisp-Utilities-and-Predicates-phundrak-zip-8a49b20f
|
||
:END:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun phundrak-zip (&rest lists)
|
||
"Zip `LISTS' together.
|
||
|
||
Be aware only the amount of elements of the smallest list will be zipped."
|
||
(declare (pure t) (side-effect-free t))
|
||
(when lists
|
||
(let ((lists (if (= 1 (length lists)) ; only one element => a list of lists was passed
|
||
(car lists)
|
||
lists)))
|
||
(when (phundrak-none? 'null lists)
|
||
(cons (mapcar 'car lists)
|
||
(phundrak-zip (mapcar 'cdr lists)))))))
|
||
#+END_SRC
|
||
|
||
*** ~phundrak-blog-publish~
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Custom-functions-macros-and-variables-phundrak-blog-publish-99c96b2d
|
||
:END:
|
||
This function is quite a simple function made to automatically publish [[https://blog.phundrak.com][my blog]]
|
||
based on Hugo. After exporting my blog using ~ox-hugo~, I simply have to call
|
||
this function which will look for all files located in =~/org/blog/public= and
|
||
copy them to my remote server once ~hugo~ has been executed in =~/org/blog=.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun phundrak-blog-publish ()
|
||
"Publish my blog through Hugo and rsync to my remote server."
|
||
(interactive)
|
||
(let* ((blog-path "~/org/blog")
|
||
(public-path (concat blog-path "/public"))
|
||
(target-path "/rsync:Tilo:/home/phundrak/www/phundrak.com/blog"))
|
||
(shell-command (format "cd %s && hugo" blog-path))
|
||
(let ((files (mapcar (lambda (file)
|
||
(f-relative file public-path))
|
||
(f-files (format "%s/public" blog-path) nil t))))
|
||
(dolist (file files)
|
||
(copy-file (concat public-path "/" file)
|
||
(concat target-path "/" file)
|
||
t nil t)))))
|
||
#+END_SRC
|
||
|
||
*** ~phundrak-yas-rust-new-assignments~
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Custom-functions-yas-rust-new-assignments-4ad16bde
|
||
:END:
|
||
The following function is a function that will allow me to easily create ~new~
|
||
functions for Rust structs. Inspired from [[https://github.com/jorgenschaefer/elpy][elpy]]’s ~elpy-snippet-init-assignments~
|
||
function, it will automatically write assignments to my new struct as I write
|
||
new parameters in the ~new~ function. It also comes with a helper function that
|
||
parses the arguments given to the ~new~ function.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun phundrak--yas-snippet-split-rust-args ($arg-string)
|
||
"Split a Rust argument string `$ARG-STRING' into ((name,
|
||
default)...) tuples"
|
||
(mapcar (lambda ($elem)
|
||
(split-string $elem "[[:blank:]]*:[[:blank:]]*" t))
|
||
(split-string $arg-string "[[:blank:]]*,[[:blank:]]*" t)))
|
||
|
||
(defun phundrak-yas-rust-new-assignments ($arg-string)
|
||
"Return a typical new assignment for arguments.
|
||
|
||
Inspired from elpy’s functions https://github.com/jorgenschaefer/elpy"
|
||
(let ((indentation (make-string (save-excursion
|
||
(goto-char start-point)
|
||
(current-indentation))
|
||
?\s)))
|
||
(mapconcat (lambda ($elem)
|
||
(if (string-match "^\\*" (car $elem))
|
||
""
|
||
(format "%s,\n%s" (car $elem) indentation)))
|
||
(phundrak--yas-snippet-split-rust-args $arg-string)
|
||
"")))
|
||
#+END_SRC
|
||
|
||
*** ~screenshot-svg~
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Custom-functions-macros-and-variables-screenshot-svg-9e2e21d2
|
||
:END:
|
||
This function allows for taking SVG screenshots of Emacs from itself using
|
||
Cairo. The function definition was taken [[https://github.com/caiohcs/my-emacs#screenshots][from here]].
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun screenshot-svg ()
|
||
"Save a screenshot of the current frame as an SVG image.
|
||
Saves to a temp file and puts the filename in the kill ring."
|
||
(interactive)
|
||
(let* ((filename (make-temp-file "Emacs" nil ".svg"))
|
||
(data (x-export-frames nil 'svg)))
|
||
(with-temp-file filename
|
||
(insert data))
|
||
(kill-new filename)
|
||
(message filename)))
|
||
#+END_SRC
|
||
|
||
*** ~xah/dired-sort~
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Custom-functions-macros-and-variables-xah-dired-sort-28bde838
|
||
:END:
|
||
This function comes directly from Xah Lee’s website and allows the user to sort
|
||
files in a dired buffer depending on four factors:
|
||
* File name
|
||
* File size
|
||
* Last modification date
|
||
* File extension
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun xah/dired-sort ()
|
||
"Sort dired dir listing in different ways. Prompt for a choice.
|
||
URL `http://ergoemacs.org/emacs/dired_sort.html'
|
||
Version 2018-12-23, modified by Phundrak on 2019-08-06"
|
||
(interactive)
|
||
(let ($sort-by $arg)
|
||
(setq $sort-by (ido-completing-read "Sort by:" '( "name" "size" "date" "extension" )))
|
||
(cond
|
||
((equal $sort-by "name") (setq $arg "-ahl --group-directories-first"))
|
||
((equal $sort-by "date") (setq $arg "-ahl -t --group-directories-first"))
|
||
((equal $sort-by "size") (setq $arg "-ahl -S --group-directories-first"))
|
||
((equal $sort-by "extension") (setq $arg "-ahlD -X --group-directories-first"))
|
||
(t (error "logic error 09535" )))
|
||
(dired-sort-other $arg )))
|
||
#+END_SRC
|
||
|
||
** Emacs builtins
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Emacs_builtins-7822b8dd
|
||
:END:
|
||
*** Dired
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Dired-ef8a7cac
|
||
:END:
|
||
When it comes to dired, I chose do modify some elements on how things are sorted
|
||
and shown, but there isn’t much configuration. First, I want to always copy
|
||
folders in a recursive way, no questions asked.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq dired-recursive-copies 'always)
|
||
#+END_SRC
|
||
|
||
Also, when I have two Dired buffers opened side by side, I generally want them
|
||
to interact with each other, for example if I want to move around or copy stuff.
|
||
So, let’s tell Emacs that:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq dired-dwim-target t)
|
||
#+END_SRC
|
||
|
||
Finally, let’s tell Dired how to sort the elements to be displayed: directories
|
||
first, non-hidden first.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq dired-listing-switches "-ahl --group-directories-first")
|
||
#+END_SRC
|
||
|
||
By the way, let’s enable ~org-download~ when we are in a Dired buffer:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(add-hook 'dired-mode-hook 'org-download-enable)
|
||
#+END_SRC
|
||
|
||
Finally, let’s enable globally ~diredfl~ so we can get a colourful Dired buffer
|
||
each time we open one:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(diredfl-global-mode 1)
|
||
#+END_SRC
|
||
|
||
*** Emacs Lisp
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Emacs-builtins-Emacs-Lisp-219d4260
|
||
:END:
|
||
For some reason, =flycheck-mode= is not enabled by default when in an elisp
|
||
buffer. Let’s add that:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(add-hook 'emacs-lisp-mode-hook 'flycheck-mode)
|
||
#+END_SRC
|
||
|
||
*** Eshell
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Eshell-3012e67e
|
||
:END:
|
||
Eshell is a built-in shell available from Emacs which I use almost as often as
|
||
Fish. Some adjustments are necessary for making this shell usable for me.
|
||
|
||
But first, here is a screenshot of what to expect visually from my configuration
|
||
of Eshell when it is launched:
|
||
|
||
#+include: ./img/eshell.svg export html
|
||
|
||
**** Aliases
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Eshell-Aliases-ef06615f
|
||
:END:
|
||
This function is a function that will come in very handy for Eshell functions
|
||
that call shell processes. It concatenates the initial string ~command~ with all
|
||
the arguments ~args~, each separated with a space.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun phundrak/concatenate-shell-command ($command &rest $args)
|
||
(string-join (cons $command $args) " "))
|
||
#+END_SRC
|
||
|
||
Just like most shells, it is possible to declare in Eshell aliases. First, I
|
||
would like to be able to use ~open~ to open files in Emacs:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defalias 'open 'find-file)
|
||
#+END_SRC
|
||
|
||
I also have ~openo~ which allows me to perform the same action, but in another
|
||
window:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defalias 'openo 'find-file-other-window)
|
||
#+END_SRC
|
||
|
||
The function ~yes-or-no-p~ is also aliased to ~y-or-n-p~ so I only have to
|
||
answer by ~y~ or ~n~ instead of typing ~yes~ or ~no~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defalias 'yes-or-no-p 'y-or-n-p)
|
||
#+END_SRC
|
||
|
||
For some ease of use, I’ll also declare ~list-buffers~ as an alias of ~ibuffer~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defalias 'list-buffers 'ibuffer)
|
||
#+END_SRC
|
||
~mkcd~ is a function that allows me to create a directory and ~cd~ into it at
|
||
the same time.
|
||
#+begin_src emacs-lisp
|
||
(defun eshell/mkcd ($directory)
|
||
(eshell/mkdir "-p" $directory)
|
||
(cd $directory))
|
||
#+end_src
|
||
|
||
**** Custom functions
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Eshell-Custom_functions-79d98245
|
||
:END:
|
||
When I’m in Eshell, sometimes I wish to open multiple files at once in Emacs.
|
||
For this, when I have several arguments for ~find-file~, I want to be able to
|
||
open them all at once. Let’s modify ~find-file~ like so:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defadvice find-file (around find-files activate)
|
||
"Also find all files within a list of files. This even works recursively."
|
||
(if (listp filename)
|
||
(cl-loop for f in filename do (find-file f wildcards))
|
||
ad-do-it))
|
||
#+END_SRC
|
||
|
||
I also want to be able to have multiple instances of Eshell opened at once. For
|
||
that, I declared the function ~eshell-new~ that does exactly that.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun eshell-new()
|
||
"Open a new instance of eshell."
|
||
(interactive)
|
||
(eshell 'N))
|
||
#+END_SRC
|
||
|
||
***** Redirect text editors to Emacs
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Eshell-Custom_functions-Redirect_text_editors_to_Emacs-dff362c6
|
||
:END:
|
||
I still have some stupid muscle memory telling me to open ~emacs~ in the
|
||
terminal, which is stupid with Eshell since I’m already inside Emacs. So, let’s
|
||
open each file passed to the ~emacs~ command and bury the eshell buffer (we’ll
|
||
get back to it later).
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun eshell/emacs (&rest $files)
|
||
"Open a file in a new buffer. Old habits die hard"
|
||
(if $files
|
||
(mapc #'find-file
|
||
(mapcar #'expand-file-name
|
||
(eshell-flatten-list (reverse $files))))
|
||
(bury-buffer)))
|
||
#+END_SRC
|
||
|
||
**** Environment variables
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Eshell-Environment_variables-8dac73e0
|
||
:END:
|
||
Some environment variables need to be correctly set so Eshell can correctly
|
||
work. The first environment variable to be set is the ~PATH~, as I have a couple
|
||
of directories where executables are located. Let’s add them to our path.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setenv "PATH"
|
||
(concat
|
||
(getenv "HOME") "/.pub-cache/bin"
|
||
":" (getenv "HOME") "/.local/bin"
|
||
":" (getenv "HOME") "/go/bin"
|
||
":" (getenv "HOME") "/.cargo/bin"
|
||
":" (getenv "HOME") "/.gem/ruby/2.6.0/bin"
|
||
":" (getenv "PATH")))
|
||
#+END_SRC
|
||
|
||
I would also 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
|
||
|
||
Finally, I’d like to add a custom directory to the ~PKG_CONFIG_PATH~:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setenv "PKG_CONFIG_PATH" (concat
|
||
"/usr/local/lib/pkgconfig/" ":"
|
||
(getenv "PKG_CONFIG_PATH")))
|
||
#+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")
|
||
#+END_SRC
|
||
|
||
**** Eshell banner
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Emacs-builtins-Eshell-Eshell-banner-12d63d67
|
||
:END:
|
||
The code creating the Eshell banner is a bit lengthy and requires some
|
||
additional explanations that would make the following chapter [[#User_Configuration-Eshell-Eshell_theme-a06715a9][Eshell theme]] too
|
||
long. So, here it is!
|
||
|
||
The banner for Eshell will collect some system information and display them
|
||
gracefully. These pieces of information are:
|
||
- The GNU/Linux distribution running (I do not use any other OS on my computer)
|
||
- The kernel name and its version
|
||
- The machine’s hostname
|
||
- Its uptime
|
||
- Its RAM and Swap usage
|
||
- How full are its mountpoints
|
||
Some of these information can be grabbed directly from Emacs built-in functions,
|
||
but some others need to be retrieved manually. Let’s first get into it with the
|
||
mounted partitions for which we’ll define a structure:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(cl-defstruct phundrak/mounted-partitions
|
||
"Object representing a mounted partition found in the system"
|
||
path size used percent)
|
||
#+END_SRC
|
||
|
||
We’ll also define a variable setting the maximum length of a partition path
|
||
before it gets abbreviated:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defvar phundrak//eshell-banner--max-length-part 13
|
||
"Maximum length of a partition path")
|
||
#+END_SRC
|
||
|
||
Now, we can get our partitions. For this, we’ll make a call to the shell command
|
||
~df -lH~ and we’ll keep only the partitions mounted on a device stored in
|
||
~/dev~, for instance on ~/dev/sda~. And as mentioned above, if the mount path of
|
||
the partition exceeds the length specified by
|
||
~phundrak//eshell-banner--max-length-part~, it will get abbreviated by
|
||
[[#User-Configuration-Custom-functions-macros-and-variables-phundrak-abbr-path-559b46e3][~phundrak-abbr-path~]].
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun phundrak/get-mounted-partitions ()
|
||
(let ((partitions (s-split "\n"
|
||
(shell-command-to-string "df -lH")
|
||
t)))
|
||
(-keep (lambda (partition)
|
||
(let* ((partition (s-split " " partition t))
|
||
(filesystem (nth 0 partition))
|
||
(size (nth 1 partition))
|
||
(used (nth 2 partition))
|
||
(percent (nth 4 partition))
|
||
(mount (nth 5 partition)))
|
||
(when (s-prefix? "/dev" filesystem)
|
||
(make-phundrak/mounted-partitions
|
||
:path (if (> phundrak//eshell-banner--max-length-part (length mount))
|
||
mount
|
||
(phundrak-abbr-path mount t))
|
||
:size size
|
||
:used used
|
||
:percent (string-to-number (s-chop-suffix "%" percent))))))
|
||
partitions)))
|
||
#+END_SRC
|
||
|
||
We’ll need some padding for the name of the information displayed on the left
|
||
hand side of the banner. The maximum length without any partitions is eight
|
||
characters due to the text ~Hostname~, so if any partition path is longer than
|
||
this, the left padding will increase.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun phundrak//eshell-banner--get-left-pad (initial-pad partitions)
|
||
(if partitions
|
||
(let ((part-length (length (phundrak/mounted-partitions-path (car partitions)))))
|
||
(phundrak//eshell-banner--get-left-pad (if (> part-length initial-pad)
|
||
part-length
|
||
initial-pad)
|
||
(cdr partitions)))
|
||
initial-pad))
|
||
#+END_SRC
|
||
|
||
Now, Let’s set three variables that will be used in the function following this
|
||
declaration. They will be used to determine in which color a percentage should
|
||
be displayed. I’ll consider any percentage below 60% to be acceptable and
|
||
therefore displayed in green. However, starting from this threshold, I want the
|
||
user to be noticed of the usage of whatever percentage shown that it has gone up
|
||
and it should be watched and displayed in yellow. Above 75%, the user should
|
||
consider this a warning, and the percentage will be displayed in orange. Above
|
||
90%, it is considered critical and the percentage will be displayed in red.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defvar phundrak//eshell-banner--critical-percentage 90)
|
||
(defvar phundrak//eshell-banner--warning-percentage 75)
|
||
(defvar phundrak//eshell-banner--notice-percentage 60)
|
||
|
||
(defun phundrak//eshell-banner--color-percentage (percentage)
|
||
(cond
|
||
((> percentage phundrak//eshell-banner--critical-percentage)
|
||
(with-face (format "%2d" percentage) :foreground phundrak-nord11))
|
||
((> percentage phundrak//eshell-banner--warning-percentage)
|
||
(with-face (format "%2d" percentage) :foreground phundrak-nord12))
|
||
((> percentage phundrak//eshell-banner--notice-percentage)
|
||
(with-face (format "%2d" percentage) :foreground phundrak-nord13))
|
||
(t
|
||
(with-face (format "%2d" percentage) :foreground phundrak-nord14))))
|
||
#+END_SRC
|
||
|
||
This function will be used when displaying progress bars. These will be used for
|
||
displaying the Ram, Swap and partitions usage of the system, displaying the used
|
||
part in red and the free part in green. For this, we just need to know the size
|
||
of the progress bar we wish to use as well as how full it should be. Note that
|
||
the percentage should be between 0 and 100.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun phundrak//eshell-banner--progress-bar (length percentage)
|
||
(let* ((length-green (if (= 0 percentage)
|
||
0
|
||
(/ (* length percentage) 100)))
|
||
(length-red (- length length-green)))
|
||
(concat (with-face "[" :weight 'bold)
|
||
(with-face (s-repeat length-green "=")
|
||
:weight 'bold :foreground phundrak-nord14)
|
||
(with-face (s-repeat length-red "=")
|
||
:weight 'bold :foreground phundrak-nord11)
|
||
(with-face "]" :weight 'bold))))
|
||
#+END_SRC
|
||
|
||
This function will be used in two distinct functions: ~phundrak-eshell-banner~
|
||
which we will see later, and ~phundrak//eshell-banner--display-memory~ which we
|
||
will see now. This function displays information for the two types of memory we
|
||
have, RAM and Swap memory. Here is the definition of this function:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun phundrak//eshell-banner--display-memory (type used total text-padding ramp-length)
|
||
(let ((percentage (if (= used 0)
|
||
0
|
||
(/ (* 100 used) total))))
|
||
(concat (s-pad-right text-padding "." type)
|
||
": "
|
||
(phundrak//eshell-banner--progress-bar ramp-length
|
||
percentage)
|
||
(format " %6s / %-5s ("
|
||
(file-size-human-readable used)
|
||
(file-size-human-readable total))
|
||
(phundrak//eshell-banner--color-percentage
|
||
percentage)
|
||
"%)\n")))
|
||
#+END_SRC
|
||
|
||
We now need a function for displaying partitions. As you can see, it will be
|
||
quite similar to the above one:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun phundrak//eshell-banner--display-partition (part left-pad ramp-length)
|
||
(concat (s-pad-right left-pad "."
|
||
(with-face (phundrak/mounted-partitions-path part)
|
||
:weight 'bold))
|
||
": "
|
||
(phundrak//eshell-banner--progress-bar ramp-length
|
||
(phundrak/mounted-partitions-percent part))
|
||
(format " %6s / %-5s (%s%%)"
|
||
(phundrak/mounted-partitions-used part)
|
||
(phundrak/mounted-partitions-size part)
|
||
(phundrak//eshell-banner--color-percentage (phundrak/mounted-partitions-percent part)))))
|
||
#+END_SRC
|
||
|
||
And we can now build our banner! Here is our function that does exactly that:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun phundrak-eshell-banner ()
|
||
(let* ((partitions (phundrak/get-mounted-partitions))
|
||
(os (replace-regexp-in-string
|
||
".*\"\\(.+\\)\""
|
||
"\\1"
|
||
(car (-filter (lambda (line)
|
||
(s-contains? "PRETTY_NAME" line))
|
||
(s-lines (phundrak-file-to-string "/etc/os-release"))))))
|
||
(memory (-map (lambda (line)
|
||
(s-split " " line t))
|
||
(s-split "\n"
|
||
(shell-command-to-string "free -b | tail -2")
|
||
t)))
|
||
(ram (nth 0 memory))
|
||
(swap (nth 1 memory))
|
||
(ramp-length 41)
|
||
(left-pad (phundrak//eshell-banner--get-left-pad phundrak//eshell-banner--max-length-part partitions))
|
||
(right-pad 8)
|
||
(left-column-width 27))
|
||
(concat (format "%s\n" (s-repeat 79 "="))
|
||
;; OS and Kernel
|
||
(format "%s: %s%s: %s\n"
|
||
(s-pad-right left-pad "." "OS")
|
||
(s-pad-right left-column-width
|
||
" "
|
||
(with-face (s-trim os)
|
||
:weight 'bold))
|
||
(s-pad-right right-pad "." "Kernel")
|
||
(with-face (concat "Linux " operating-system-release)
|
||
:weight 'bold))
|
||
;; Hostname and Uptime
|
||
(format "%s: %s%s: %s\n"
|
||
(s-pad-right left-pad "." "Hostname")
|
||
(s-pad-right left-column-width
|
||
" "
|
||
(with-face (system-name) :weight 'bold))
|
||
(s-pad-right right-pad "." "Uptime")
|
||
(with-face (s-chop-prefix "up "
|
||
(s-trim (shell-command-to-string "uptime -p")))
|
||
:weight 'bold))
|
||
;; RAM ramp
|
||
(phundrak//eshell-banner--display-memory "Ram"
|
||
(string-to-number (nth 2 ram))
|
||
(string-to-number (nth 1 ram))
|
||
left-pad
|
||
ramp-length)
|
||
;; SWAP ramp
|
||
(phundrak//eshell-banner--display-memory "Swap"
|
||
(string-to-number (nth 2 swap))
|
||
(string-to-number (nth 1 swap))
|
||
left-pad
|
||
ramp-length)
|
||
;; Partitions
|
||
(mapconcat (lambda (part)
|
||
(phundrak//eshell-banner--display-partition part left-pad ramp-length))
|
||
partitions
|
||
"\n")
|
||
(format "\n%s\n" (s-repeat 79 "=")))))
|
||
#+END_SRC
|
||
|
||
We now only have to set the result of this function as our Eshell banner. Since
|
||
a simple ~setq~ would only run ~phundrak-eshell-banner~ once when Emacs starts,
|
||
we’ll actually make Emacs set the value of ~eshell-banner-message~ each time it
|
||
is required by Eshell with a hook:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(add-hook 'eshell-banner-load-hook
|
||
(lambda ()
|
||
(setq eshell-banner-message (phundrak-eshell-banner))))
|
||
#+END_SRC
|
||
|
||
**** Eshell theme
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Eshell-Eshell_theme-a06715a9
|
||
:END:
|
||
As with most shells, again, it is possible to customize the appearance of the
|
||
Eshell prompt. As you can see, my prompt has some Nord colors, a shortened path,
|
||
a git prompt, and an indicator of whether the previous command succeeded or
|
||
failed. Note however that the abbreviation of the current path depends on the
|
||
value of ~phundrak-prompt--abbreviate~, if it is ~t~ it is abbreviated;
|
||
otherwise, it is kept in full. It can be toggled with a keyboard shortcut, see
|
||
[[#User_Configuration-Shortcuts-Toggle-d53c27ef][Keybindings: Toggle]].
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun phundrak-eshell-prompt ()
|
||
"Definition of my prompt for Eshell
|
||
|
||
It displays a powerline prompt, with first an abbreviated path to
|
||
the current directory. If `phundrak-prompt--abbreviate' is `t',
|
||
then all preceding directories will be abbreviated to one
|
||
character, except hidden directory which first character will be
|
||
preceded by a dot. Otherwise, the full name of the directories is
|
||
displayed.
|
||
|
||
Then, if the current directory is a git repository or one of its
|
||
subdirectories, it will display the current state of the
|
||
repository. See `phundrak-eshell-git-status'
|
||
|
||
Finally, a lambda character is displayed, either in blue or in
|
||
red depending on if the last eshell command was a success or a
|
||
failure respectively."
|
||
(let* ((header-bg phundrak-nord0)
|
||
($path (phundrak-abbr-path (eshell/pwd)))
|
||
($git-path (phundrak-git-repo-root $path))
|
||
($abbr-path (phundrak-abbr-path $path phundrak-prompt--abbreviate))
|
||
($background phundrak-nord1)
|
||
($foreground phundrak-nord14)
|
||
($success phundrak-nord10)
|
||
($error phundrak-nord11))
|
||
(concat (with-face (concat " "
|
||
(phundrak-abbr-path (phundrak-var-or-if-nil $git-path $path)
|
||
phundrak-prompt--abbreviate)
|
||
" ")
|
||
:foreground $foreground
|
||
:background $background)
|
||
(when $git-path
|
||
(concat (phundrak-eshell-git-status $path $background)
|
||
(with-face (format "%s "
|
||
(let (($in-git-path (phundrak-abbr-path (f-relative $path $git-path)
|
||
phundrak-prompt--abbreviate)))
|
||
(if (string= "." $in-git-path)
|
||
""
|
||
(concat " " $in-git-path))))
|
||
:foreground $foreground
|
||
:background $background)))
|
||
(with-face "λ "
|
||
:foreground (if (zerop eshell-last-command-status)
|
||
$success
|
||
$error)
|
||
:background $background)
|
||
(with-face "" :foreground $background)
|
||
" ")))
|
||
#+END_SRC
|
||
|
||
Now, let’s declare our prompt regexp and our prompt functions:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq eshell-prompt-regexp "^[^\n]*λ "
|
||
eshell-prompt-function 'phundrak-eshell-prompt)
|
||
#+END_SRC
|
||
|
||
I also don't want the banner to be displayed, I know I entered the Elisp shell,
|
||
no need to remind me. Maybe I’ll do something with it one day.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq eshell-banner-message "")
|
||
#+END_SRC
|
||
|
||
Finally, let’s enable some fish-like syntax highlighting:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(eshell-syntax-highlighting-global-mode +1)
|
||
#+END_SRC
|
||
|
||
**** Visual commands
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Eshell-Visual_commands-2b15e0dc
|
||
:END:
|
||
With Eshell, some commands don’t work very well, especially commands that create
|
||
a TUI. So, let’s declare them as visual commands or subcommands:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq eshell-visual-commands
|
||
'("fish" "zsh" "bash" "tmux" "htop" "top" "vim" "bat" "nano")
|
||
eshell-visual-subcommands
|
||
'("git" "log" "l" "diff" "show"))
|
||
#+END_SRC
|
||
|
||
*** Org-mode
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Org-mode-04ab8ad3
|
||
:header-args:emacs-lisp: :tangle no :exports code :results silent
|
||
:END:
|
||
Org-mode is probably one of the best if not the best Emacs feature I have ever
|
||
discovered. It is awesome for writing documents, regardless of the format you
|
||
need it to be exported to, for agenda management, and for literary programming,
|
||
such as with this document.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(with-eval-after-load 'org
|
||
;; configuration goes here
|
||
)
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC emacs-lisp :tangle ~/.config/emacs/private/user-config.el :exports none :noweb yes
|
||
(with-eval-after-load 'org
|
||
;; agenda
|
||
<<org-agenda-files>>
|
||
<<org-agenda-commands>>
|
||
;; Babel
|
||
<<org-babel-load-languages>>
|
||
<<org-babel-set-geiser>>
|
||
<<org-src-tab-acts-natively>>
|
||
<<org-confirm-babel>>
|
||
;; Beautiful org
|
||
<<beautiful-org-hooks>>
|
||
<<beautiful-org-options>>
|
||
<<beautiful-org-fontify-blocks>>
|
||
<<beautiful-org-fontify-inline>>
|
||
<<beautiful-org-images>>
|
||
<<beautiful-org-latex-inline>>
|
||
<<beautiful-org-latex-inline-transparent>>
|
||
<<beautiful-org-macro-ellipsis-markers>>
|
||
<<beautiful-org-hide-dots>>
|
||
;; Capture
|
||
<<org-capture-target-files>>
|
||
<<org-capture-templates>>
|
||
;; Custom functions
|
||
;;;; Better IDs
|
||
<<org-better-id-new>>
|
||
<<org-better-id-get>>
|
||
<<org-better-id-add-ids>>
|
||
<<org-better-id-hooks>>
|
||
;; File export
|
||
<<org-use-sub-superscripts>>
|
||
;;;; Latex
|
||
<<org-latex-compiler>>
|
||
<<org-latex-listings>>
|
||
<<org-latex-default-packages>>
|
||
<<org-export-latex-hyperref-format>>
|
||
<<org-latex-pdf-process>>
|
||
;;;; HTML
|
||
<<org-re-reveal-root>>
|
||
<<org-html-validation>>
|
||
;; Latex Formats
|
||
<<org-latex-classes>>
|
||
;; Projects
|
||
<<org-publish-projects>>
|
||
;; Variables
|
||
;;;; Behavior
|
||
<<org-use-property-inheritance>>
|
||
<<org-list-allow-alphabetical>>
|
||
<<org-src-block-lsp>>
|
||
<<org-M-RET-may-split-line>>
|
||
<<org-src-window-setup>>
|
||
<<org-src-window-toggle>>
|
||
<<org-id-link-to-org>>
|
||
;;;; User information
|
||
<<org-user-information>>
|
||
)
|
||
#+END_SRC
|
||
|
||
**** Agenda
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Org-mode-Org_agenda-53f9d319
|
||
:END:
|
||
One awesome feature of Org mode is the agenda. By default, my agendas are stored
|
||
in =~/org/agenda=.
|
||
#+NAME: org-agenda-files
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-agenda-files (list "~/org/agenda" "~/org/notes.org"))
|
||
#+END_SRC
|
||
|
||
I also have a custom command in Org agenda to mark some tasks as daily tasks
|
||
with the =:DAILY:= tag,:
|
||
#+NAME: org-agenda-custom-commands
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-agenda-custom-commands
|
||
'(("h" "Daily habits"
|
||
((agenda ""))
|
||
((org-agenda-show-log t)
|
||
(org-agenda-ndays 7)
|
||
(org-agenda-log-mode-items '(state))
|
||
(org-agenda-skip-function
|
||
'(org-agenda-skip-entry-if 'notregexp
|
||
":DAILY:"))))
|
||
("Y" "Yearly events"
|
||
((agenda ""))
|
||
((org-agenda-show-log t)
|
||
(org-agenda-ndays 365)
|
||
(org-agenda-log-mode-items '(state))
|
||
(org-agenda-skip-entry-if 'notregexp
|
||
":YEARLY:")))))
|
||
#+END_SRC
|
||
|
||
**** Babel
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Org-mode-Org_babel_languages-c062fc16
|
||
:END:
|
||
One of the amazing features of org-mode is its literary programming capacities
|
||
by running code blocks from within Org-mode itself. But for that, only a couple
|
||
of languages are supported directly by Org-mode itself, and they need to be
|
||
activated. Here are the languages I activated in my Org-mode configuration:
|
||
#+NAME: org-babel-languages-table
|
||
| C |
|
||
| dot |
|
||
| emacs-lisp |
|
||
| gnuplot |
|
||
| java |
|
||
| latex |
|
||
| latex-as-png |
|
||
| makefile |
|
||
| plantuml |
|
||
| python |
|
||
| restclient |
|
||
| sass |
|
||
| scheme |
|
||
| shell |
|
||
|
||
#+NAME: org-babel-languages-gen
|
||
#+header: :cache yes :results replace
|
||
#+header: :var languages=org-babel-languages-table[,0]
|
||
#+BEGIN_SRC emacs-lisp :exports none
|
||
(format "'(%s)"
|
||
(mapconcat (lambda ($language)
|
||
(format "(%s . t)" $language))
|
||
languages
|
||
"\n "))
|
||
#+END_SRC
|
||
|
||
#+RESULTS[27e5ca97338f4a5e6874ece62f4e34c854284c59]: org-babel-languages-gen
|
||
#+begin_example
|
||
'((C . t)
|
||
(dot . t)
|
||
(emacs-lisp . t)
|
||
(gnuplot . t)
|
||
(java . t)
|
||
(latex . t)
|
||
(latex-as-png . t)
|
||
(makefile . t)
|
||
(plantuml . t)
|
||
(python . t)
|
||
(restclient . t)
|
||
(sass . t)
|
||
(scheme . t)
|
||
(shell . t))
|
||
#+end_example
|
||
|
||
The corresponding code is as follows:
|
||
#+NAME: org-babel-load-languages
|
||
#+BEGIN_SRC emacs-lisp :noweb yes
|
||
(org-babel-do-load-languages
|
||
'org-babel-load-languages
|
||
<<org-babel-languages-gen()>>)
|
||
#+END_SRC
|
||
|
||
Scheme requires a default implementation for geiser:
|
||
#+NAME: org-babel-set-geiser
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq geiser-default-implementation 'racket)
|
||
#+END_SRC
|
||
|
||
By the way, I wish to see source code behave the same way in the source blocks
|
||
as in their own major mode. Let’s tell Emacs so:
|
||
#+NAME: org-src-tab-acts-natively
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-src-tab-acts-natively t)
|
||
#+END_SRC
|
||
|
||
Lastly, I know this can be a terrible idea, but I want Emacs to just evaluate
|
||
Org code blocks without asking me. Of course, this could represent some big
|
||
security issue if not careful enough, but I generaly just open my own org files.
|
||
#+NAME: org-confirm-babel
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-confirm-babel-evaluate nil)
|
||
#+END_SRC
|
||
|
||
**** Beautify Org-mode
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Beautify-Org-mode-0506af2f
|
||
:END:
|
||
As I will always say, orgmode is an amazing piece of software that deserves
|
||
particular care and love. That is why I want to give it a unique look and feel
|
||
compared to the rest of my Emacs configuration, in order to make it feel much
|
||
more comfortable. You will find below how my org buffers look like when I open
|
||
one of them.
|
||
|
||
#+begin_export html
|
||
<p><img src="./img/org-mode.svg" alt="Screenshot of an org-mode buffer" style="max-height: 900px" /></p>
|
||
#+end_export
|
||
|
||
And here you can find the source code of this buffer you just saw a screenshot
|
||
of:
|
||
#+BEGIN_SRC org :exports code
|
||
,#+TITLE: My Document
|
||
,#+AUTHOR: Lucien Cartier-Tilet
|
||
,#+EMAIL: lucien@phundrak.com
|
||
,#+DATE: 2020-12-21
|
||
,#+TAGS: tag1|tag2|tag3
|
||
,* Header 1
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: custom-id-for-html-export
|
||
:END:
|
||
# A regular comment
|
||
This is an example org file, with [[https://config.phundrak.com/emacs][a link]], a {{{macro(with arguments)}}}, an <<anchor>>, /text in italic/, __undelined__, *bold*, +striken through+, some ~code~ and =verbatim=, some^{superscript} and_{subscript}, a code block and a bullet list:
|
||
,* Element 1
|
||
,* Element 2
|
||
,#+header :export code
|
||
,#+BEGIN_SRC emacs-lisp
|
||
(message "Hello World!")
|
||
,#+END_SRC
|
||
| / | <r> | <c> | < |
|
||
| This | is | a | table |
|
||
|------+---------+----------+-------|
|
||
| With | various | elements | |
|
||
,** TODO Global task [1/2]
|
||
,*** TODO [#A] Task 1
|
||
,*** TODO Checkboxes [1/3]
|
||
- [ ] Checkbox 1
|
||
- [X] Checkbox 2
|
||
- [ ] Checkbox 3
|
||
,*** DONE Task 2
|
||
CLOSED: [2020-12-21 mon. 14:30]
|
||
,** Header 2
|
||
,*** Header 3
|
||
,**** Header 4
|
||
,***** Header 5
|
||
,****** Header 6
|
||
,******* Header 7
|
||
,******** Header 8
|
||
#+END_SRC
|
||
|
||
In order to make org-mode even sexier, let’s enable ~variable-pitch-mode~ for
|
||
org-mode so we can get some proportional font. I’ll also remove ~auto-fill-mode~
|
||
which seems to stick to Orgmode like hell and I don’t know why.
|
||
#+NAME: beautiful-org-hooks
|
||
#+BEGIN_SRC emacs-lisp
|
||
(add-hook 'org-mode-hook 'visual-line-mode)
|
||
(remove-hook 'org-mode-hook 'auto-fill-mode)
|
||
(add-hook 'org-mode-hook 'variable-pitch-mode)
|
||
(auto-fill-mode -1)
|
||
#+END_SRC
|
||
|
||
You can then see the modified faces for org-mode [[#User-Configuration-Visual-configuration-Better-faces-Org-mode-07754177][here]].
|
||
|
||
By default, I would like my org-mode buffers to be indented and tables to be
|
||
aligned.
|
||
#+NAME: beautiful-org-options
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-startup-indented t
|
||
org-startup-align-all-tables t)
|
||
#+END_SRC
|
||
|
||
***** Fontifying parts of org-mode
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Emacs-builtins-Org-mode-Beautify-Org-mode-Fontifying-parts-of-org-mode-f690ee40
|
||
:END:
|
||
Some blocks of org-mode should have their own face, such as the whole heading
|
||
line, the done headline, the quote and the verse blocks,… actually, let’s enable
|
||
that for all of them.
|
||
#+name: beautiful-org-fontify-blocks
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-pretty-entities t
|
||
org-fontify-whole-heading-line t
|
||
org-fontify-done-headline t
|
||
org-fontify-quote-and-verse-blocks t)
|
||
#+END_SRC
|
||
|
||
***** Fontifying inline src blocks
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Emacs-builtins-Org-mode-Beautify-Org-mode-Fontifying-inline-src-blocks-bf879724
|
||
:END:
|
||
When it comes to source blocks in org-mode, Emacs handle them really well with
|
||
some beautiful syntax highlight thanks to the the languages’ major mode and
|
||
their font-locks. But inline src blocks are the forgotten child and get next to
|
||
no love, which is really sad ; I want it to feel loved, to stand out from the
|
||
crowd and to give me what its brother gives me already!
|
||
|
||
Enters [[https://tecosaur.github.io/emacs-config/config.html#fontifying-inline-src][Tecosaur’s config]]! With ~org-src-font-lock-fontify-block~, anything’s
|
||
possible! And ~{{{results(...)}}}~ can also have the ~org-block~ face applied to
|
||
match and make org-mode even more beautiful! Let’s do it:
|
||
#+name: beautiful-org-fontify-inline
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defvar org-prettify-inline-results t
|
||
"Whether to use (ab)use prettify-symbols-mode on
|
||
{{{results(...)}}}.")
|
||
|
||
(defun org-fontify-inline-src-blocks (limit)
|
||
"Try to apply `org-fontify-inline-src-blocks-1'."
|
||
(condition-case nil
|
||
(org-fontify-inline-src-blocks-1 limit)
|
||
(error (message "Org mode fontification error in %S at %d"
|
||
(current-buffer)
|
||
(line-number-at-pos)))))
|
||
|
||
(defun org-fontify-inline-src-blocks-1 (limit)
|
||
"Fontify inline src_LANG blocks, from `point' up to `LIMIT'."
|
||
(let ((case-fold-search t))
|
||
(when
|
||
; stolen from `org-element-inline-src-block-parser'
|
||
(re-search-forward "\\_<src_\\([^ \t\n[{]+\\)[{[]?" limit t)
|
||
(let ((beg (match-beginning 0))
|
||
pt
|
||
(lang-beg (match-beginning 1))
|
||
(lang-end (match-end 1)))
|
||
(remove-text-properties beg lang-end '(face nil))
|
||
(font-lock-append-text-property lang-beg lang-end 'face 'org-meta-line)
|
||
(font-lock-append-text-property beg lang-beg 'face 'shadow)
|
||
(font-lock-append-text-property beg lang-end 'face 'org-block)
|
||
(setq pt (goto-char lang-end))
|
||
(when (org-element--parse-paired-brackets ?\[)
|
||
(remove-text-properties pt (point) '(face nil))
|
||
(font-lock-append-text-property pt
|
||
(point)
|
||
'face
|
||
'org-block)
|
||
(setq pt (point)))
|
||
(when (org-element--parse-paired-brackets ?\{)
|
||
(remove-text-properties pt (point) '(face nil))
|
||
(font-lock-append-text-property pt
|
||
(1+ pt)
|
||
'face
|
||
'(org-block shadow))
|
||
(unless (= (1+ pt) (1- (point)))
|
||
(if org-src-fontify-natively
|
||
(org-src-font-lock-fontify-block
|
||
(buffer-substring-no-properties lang-beg
|
||
lang-end)
|
||
(1+ pt)
|
||
(1- (point)))
|
||
(font-lock-append-text-property (1+ pt)
|
||
(1- (point))
|
||
'face
|
||
'org-block)))
|
||
(font-lock-append-text-property (1- (point))
|
||
(point)
|
||
'face
|
||
'(org-block shadow))
|
||
(setq pt (point)))
|
||
(when (and org-prettify-inline-results
|
||
(re-search-forward "\\= {{{results(" limit t))
|
||
(font-lock-append-text-property pt
|
||
(1+ pt)
|
||
'face
|
||
'org-block)
|
||
(goto-char pt))))
|
||
(when (and org-prettify-inline-results
|
||
(re-search-forward "{{{results(\\(.+?\\))}}}"
|
||
limit t))
|
||
(remove-list-of-text-properties (match-beginning 0)
|
||
(point)
|
||
'(composition prettify-symbols-start prettify-symbols-end))
|
||
(font-lock-append-text-property (match-beginning 0)
|
||
(match-end 0)
|
||
'face
|
||
'org-block)
|
||
(let ((start (match-beginning 0))
|
||
(end (match-beginning 1)))
|
||
(with-silent-modifications (compose-region start end "⟨")
|
||
(add-text-properties start
|
||
end
|
||
`(prettify-symbols-start ,start prettify-symbols-end
|
||
,end))))
|
||
(let ((start (match-end 1))
|
||
(end (point)))
|
||
(with-silent-modifications (compose-region start end "⟩")
|
||
(add-text-properties start
|
||
end
|
||
`(prettify-symbols-start ,start prettify-symbols-end
|
||
,end)))))))
|
||
|
||
(defun org-fontify-inline-src-blocks-enable ()
|
||
"Add inline src fontification to font-lock in Org.
|
||
Must be run as part of `org-font-lock-set-keywords-hook'."
|
||
(setq org-font-lock-extra-keywords
|
||
(append org-font-lock-extra-keywords '((org-fontify-inline-src-blocks)))))
|
||
|
||
(add-hook 'org-font-lock-set-keywords-hook #'org-fontify-inline-src-blocks-enable)
|
||
#+END_SRC
|
||
|
||
***** Images in org-mode
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Emacs-builtins-Org-mode-Beautify-Org-mode-Images-in-org-mode-8cf9bb6c
|
||
:END:
|
||
By default, images should be displayed inline, but not with a too large width. I
|
||
found that 550px fits well, since that is roughly the average width of the text
|
||
when ~org-fill-paragraph~ is called. Let’s also tell org-mode to display images
|
||
as inline images and redisplay them when needed.
|
||
#+NAME: beautiful-org-images
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-image-actual-width 550
|
||
org-redisplay-inline-images t
|
||
org-display-inline-images t
|
||
org-startup-with-inline-images "inlineimages")
|
||
#+END_SRC
|
||
|
||
***** Prettier LaTeX inline rendering
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Emacs-builtins-Org-mode-Beautify-Org-mode-Prettier-LaTeX-inline-rendering-66605d3c
|
||
:END:
|
||
[[https://tecosaur.github.io/emacs-config/config.html#prettier-rendering][Tecosaur strikes again]]! Let’s admit it, inline LaTeX code looks cool, properly
|
||
formatted LaTeX inline fragments look rad! Let’s fix their appearance:
|
||
#+name: beautiful-org-latex-inline
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-format-latex-header "\\documentclass{article}
|
||
\\usepackage[usenames]{color}
|
||
|
||
\\usepackage[T1]{fontenc}
|
||
|
||
\\usepackage{booktabs}
|
||
|
||
\\pagestyle{empty} % do not remove
|
||
% The settings below are copied from fullpage.sty
|
||
\\setlength{\\textwidth}{\\paperwidth}
|
||
\\addtolength{\\textwidth}{-3cm}
|
||
\\setlength{\\oddsidemargin}{1.5cm}
|
||
\\addtolength{\\oddsidemargin}{-2.54cm}
|
||
\\setlength{\\evensidemargin}{\\oddsidemargin}
|
||
\\setlength{\\textheight}{\\paperheight}
|
||
\\addtolength{\\textheight}{-\\headheight}
|
||
\\addtolength{\\textheight}{-\\headsep}
|
||
\\addtolength{\\textheight}{-\\footskip}
|
||
\\addtolength{\\textheight}{-3cm}
|
||
\\setlength{\\topmargin}{1.5cm}
|
||
\\addtolength{\\topmargin}{-2.54cm}
|
||
% my custom stuff
|
||
\\usepackage[nofont,plaindd]{bmc-maths}
|
||
\\usepackage{arev}
|
||
")
|
||
#+END_SRC
|
||
|
||
And I much prefer when LaTeX fragments are transparent, so let’s make them.
|
||
#+name: beautiful-org-latex-inline-transparent
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-format-latex-options
|
||
(plist-put org-format-latex-options :background "Transparent"))
|
||
#+END_SRC
|
||
|
||
***** Symbols
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Emacs-builtins-Org-mode-Beautify-Org-mode-Symbols-042d7374
|
||
:END:
|
||
I visually prefer to have a nicer folding icon in Emacs and the markers of macros hidden.
|
||
#+NAME: beautiful-org-macro-ellipsis-markers
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-hide-macro-markers t
|
||
org-ellipsis " ")
|
||
#+END_SRC
|
||
|
||
I also have an issue where small dots precede my org headers. Let’s fix that:
|
||
#+NAME: beautiful-org-hide-dots
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-hide-leading-stars nil
|
||
org-superstar-leading-bullet ?\s)
|
||
#+END_SRC
|
||
|
||
***** TODO Fix org-appear :noexport:
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Emacs-builtins-Org-mode-Beautify-Org-mode-Fix-org-appear-709f555f
|
||
:END:
|
||
**** Behavior
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Org-mode-Org_variables-Org_behavior-0319db38
|
||
:END:
|
||
Something really neat I learned about is the ability of org headers to inherit
|
||
properties from parent headers. Let’s enable that!
|
||
#+NAME: org-use-property-inheritance
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-use-property-inheritance t)
|
||
#+END_SRC
|
||
|
||
Sometimes, I also want to have alphabetical lists in org-mode:
|
||
#+NAME: org-list-allow-alphabetical
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-list-allow-alphabetical t)
|
||
#+END_SRC
|
||
|
||
LSP can work in source blocks, but some work is needed (shamelessly stolen [[https://tecosaur.github.io/emacs-config/config.html#lsp-support-src][from
|
||
here]], though modified a tiny bit). Here are the languages I want to activate LSP
|
||
for in this environment:
|
||
#+NAME: org-lsp-languages-src-blocks-table
|
||
| c |
|
||
| c++ |
|
||
| dart |
|
||
| python |
|
||
| rust |
|
||
|
||
#+NAME: org-lsp-languages-src-blocks-gen
|
||
#+header: :cache yes :results replace
|
||
#+header: :var languages=org-lsp-languages-src-blocks-table[,0]
|
||
#+BEGIN_SRC emacs-lisp :exports none
|
||
(mapconcat (lambda (lang)
|
||
(format "\"%s\"" lang))
|
||
languages
|
||
" ")
|
||
#+END_SRC
|
||
|
||
#+RESULTS[cbfba838da4510647d09bfd0fd2f20996c8cad38]: org-lsp-languages-src-blocks-gen
|
||
: "c" "c++" "dart" "python" "rust"
|
||
|
||
And here is the code to activate that:
|
||
#+NAME: org-src-block-lsp
|
||
#+BEGIN_SRC emacs-lisp :noweb yes
|
||
(cl-defmacro lsp-org-babel-enable (lang)
|
||
"Support LANG in org source code block."
|
||
(setq centaur-lsp 'lsp-mode)
|
||
(cl-check-type lang stringp)
|
||
(let* ((edit-pre (intern (format "org-babel-edit-prep:%s" lang)))
|
||
(intern-pre (intern (format "lsp--%s" (symbol-name edit-pre)))))
|
||
`(progn
|
||
(defun ,intern-pre (info)
|
||
(let ((file-name (->> info caddr (alist-get :file))))
|
||
(unless file-name
|
||
(setq file-name (make-temp-file "babel-lsp-")))
|
||
(setq buffer-file-name file-name)
|
||
(lsp-deferred)))
|
||
(put ',intern-pre 'function-documentation
|
||
(format "Enable lsp-mode in the buffer of org source block (%s)."
|
||
(upcase ,lang)))
|
||
(if (fboundp ',edit-pre)
|
||
(advice-add ',edit-pre :after ',intern-pre)
|
||
(progn
|
||
(defun ,edit-pre (info)
|
||
(,intern-pre info))
|
||
(put ',edit-pre 'function-documentation
|
||
(format "Prepare local buffer environment for org source block (%s)."
|
||
(upcase ,lang))))))))
|
||
(defvar org-babel-lsp-lang-list
|
||
'(<<org-lsp-languages-src-blocks-gen()>>))
|
||
(dolist (lang org-babel-lsp-lang-list)
|
||
(eval `(lsp-org-babel-enable ,lang)))
|
||
#+END_SRC
|
||
|
||
Here is one behavior that I really want to see modified: the ability to use
|
||
~M-RET~ without slicing the text the marker is on.
|
||
#+NAME: org-M-RET-may-split-line
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-M-RET-may-split-line nil)
|
||
#+END_SRC
|
||
|
||
Since Org 9.3, Org no longer attempts to restore the window configuration in the
|
||
frame to which the user returns after editing a source block with
|
||
~org-edit-src-code~. This means with the original value of
|
||
~org-src-window-setup~ (~reorganize-frame~), the current frame will be split in
|
||
two between the original org window and the source window, and once we quit the
|
||
source window only the org window will remain. This is not a desired behavior
|
||
for me, so I chose to set this variable to ~split-window-right~ in order to keep
|
||
my windows organization and have a similar behavior to the old one.
|
||
#+NAME: org-src-window-setup
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-src-window-setup 'split-window-below)
|
||
#+END_SRC
|
||
|
||
However, it is not rare that I want to change that for an horizontal split,
|
||
which can be achieved with the value ~split-window-below~. Thus, I have made
|
||
this function that allows me to switch between the (default) vertical split and
|
||
the horizontal split.
|
||
#+NAME: org-src-window-toggle
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun phundrak/toggle-org-src-window-split ()
|
||
"This function allows the user to toggle the behavior of
|
||
`org-edit-src-code'. If the variable `org-src-window-setup' has
|
||
the value `split-window-right', then it will be changed to
|
||
`split-window-below'. Otherwise, it will be set back to
|
||
`split-window-right'"
|
||
(interactive)
|
||
(if (equal org-src-window-setup 'split-window-right)
|
||
(setq org-src-window-setup 'split-window-below)
|
||
(setq org-src-window-setup 'split-window-right))
|
||
(message "Org-src buffers will now split %s"
|
||
(if (equal org-src-window-setup 'split-window-right)
|
||
"vertically"
|
||
"horizontally")))
|
||
#+END_SRC
|
||
|
||
When creating a link to an Org flie, I want to create an ID only if the link is
|
||
created interactively, and only if there is no custom ID already created.
|
||
#+NAME: org-id-link-to-org
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-id-link-to-org-use-id 'create-if-interactive-and-no-custom-id)
|
||
#+END_SRC
|
||
|
||
The tag ~:noexport:~ is fine and all, but it doesn’t allow for hidden org
|
||
structures, that is headers that are visible in the org buffer but once the file
|
||
is exported to another format the header disappears but its content stays.
|
||
~ox-extra~ has such a feature through ~ignore-headlines~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(require 'ox-extra)
|
||
(ox-extras-activate '(ignore-headlines))
|
||
#+END_SRC
|
||
This gives us access to the ~:ignore:~ tag which allows the behavior above
|
||
mentioned. To give you an idea, the org buffer
|
||
#+BEGIN_SRC org
|
||
,* Headline 1
|
||
Blah
|
||
|
||
,** Headline 2
|
||
Blah
|
||
|
||
,*** Hidden headline 3-1 :ignore:
|
||
Blabla
|
||
|
||
,*** Hidden headline 3-2 :ignore:
|
||
Blahblah
|
||
#+END_SRC
|
||
Will be exported as if it were the buffer
|
||
#+BEGIN_SRC org
|
||
,* Headline 1
|
||
Blah
|
||
|
||
,** Headline 2
|
||
Blah
|
||
|
||
Blabla
|
||
|
||
Blahblah
|
||
#+END_SRC
|
||
|
||
**** Capture
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Org-mode-Org_capture-f58979cf
|
||
:header-args:org: :mkdirp yes :padline no
|
||
:END:
|
||
Org-capture is an amazing feature of Org-mode which allows me to quickly save
|
||
links, resources, reminders, and notes in neatly organized org files. Here they
|
||
are described:
|
||
#+NAME: org-capture-target-files
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defvar org-conlanging-file "~/org/conlanging.org")
|
||
(defvar org-default-notes-file "~/org/notes.org")
|
||
(defvar org-journal-file "~/org/journal.org")
|
||
(defvar org-linguistics-notes-file "~/org/linguistics-notes.org")
|
||
(defvar org-novel-notes-file "~/org/novel-notes.org")
|
||
(defvar org-private-agenda-file "~/org/agenda/private.org")
|
||
(defvar org-school-agenda-file "~/org/agenda/school.org")
|
||
(defvar org-wordbuilding-file "~/org/worldbuilding.org")
|
||
#+END_SRC
|
||
|
||
With Spacemacs, an Org capture can be invoked with the shortcut ~SPC a o c~. It
|
||
will then ask which template I wish to use. In the table below are described the
|
||
shortcuts that are available after ~SPC a o c~ is invoked. The /name/ will be
|
||
the one displayed in Org capture’s interface, the /title/ is the headline where
|
||
to save the capture (if it does not differ from the capture’s name, the cell
|
||
will be blank). The /insertion mode/ tells Emacs how to add the capture to the
|
||
/file/, using which /template/. A line with no insertion mode, file, or template
|
||
is just a category. All of the following insert entries to their org files, that
|
||
is a new org node with a headline and some content.
|
||
#+NAME: org-capture-shortcuts-table
|
||
| Shortcut | Name | Title | Insertion mode | file | template |
|
||
|----------+---------------+-----------+----------------+-------------------------+--------------------------|
|
||
| e | Email | | | | |
|
||
| ew | Write Email | Emails | file+headline | org-default-notes-file | emails.orgcaptmpl |
|
||
| j | Journal | | file+datetree | org-journal-file | journal.orgcaptmpl |
|
||
| l | Link | | | | |
|
||
| ll | General | | file+headline | org-default-notes-file | link.orgcaptmpl |
|
||
| ly | YouTube | | file+headline | org-default-notes-file | youtube.orgcaptmpl |
|
||
| L | Protocol Link | Link | file+headline | org-default-notes-file | protocol-link.orgcaptmpl |
|
||
| n | Notes | | | | |
|
||
| nc | Conlanging | Note | file+headline | org-conlanging-file | notes.orgcaptmpl |
|
||
| nn | General | | file+headline | org-default-notes-file | notes.orgcaptmpl |
|
||
| nN | Novel | Note | file+headline | org-novel-notes-file | notes.orgcaptmpl |
|
||
| nq | Quote | | file+headline | org-default-notes-file | notes-quote.orgcaptmpl |
|
||
| nw | Worldbuilding | Note | file+headline | org-wordbuilding-file | notes.orgcaptmpl |
|
||
| N | Novel | | | | |
|
||
| Ni | Ideas | | file+headline | org-novel-notes-file | notes.orgcaptmpl |
|
||
| p | Protocol | Link | file+headline | org-default-notes-file | protocol.orgcaptmpl |
|
||
| r | Resources | | | | |
|
||
| rc | Conlanging | Resources | file+headline | org-conlanging-file | resource.orgcaptmpl |
|
||
| re | Emacs | | file+headline | org-default-notes-file | resource.orgcaptmpl |
|
||
| ri | Informatique | | file+headline | org-default-notes-file | resource.orgcaptmpl |
|
||
| rl | Linguistics | | file+headline | org-default-notes-file | resource.orgcaptmpl |
|
||
| rL | Linux | | file+headline | org-default-notes-file | resource.orgcaptmpl |
|
||
| rw | Worldbuilding | Resources | file+headline | org-wordbuilding-file | resource.orgcaptmpl |
|
||
| t | Tasks | | | | |
|
||
| tb | Birthday | | file+headline | org-private-agenda-file | birthday.orgcaptmpl |
|
||
| te | Event | | file+headline | org-private-agenda-file | event.orgcaptmpl |
|
||
| th | Health | | file+headline | org-private-agenda-file | health.orgcaptmpl |
|
||
| ti | Informatique | | file+headline | org-private-agenda-file | informatique.orgcaptmpl |
|
||
|
||
#+NAME: org-capture-shortcut-gen
|
||
#+header: :exports none :cache yes :results replace
|
||
#+BEGIN_SRC emacs-lisp :var table=org-capture-shortcuts-table
|
||
(format "'(%s)"
|
||
(mapconcat (lambda (entry)
|
||
(let* ((shortcut (nth 0 entry))
|
||
(name (nth 1 entry))
|
||
(title (nth 2 entry))
|
||
(insertmode (nth 3 entry))
|
||
(fileinsert (nth 4 entry))
|
||
(sourceorg (nth 5 entry)))
|
||
(if (string= "" insertmode)
|
||
(format "(\"%s\" \"%s\")" shortcut name)
|
||
(concat (format "(\"%s\" \"%s\" entry\n" shortcut name)
|
||
(format " (%s %s%s)\n" insertmode fileinsert
|
||
(if (string= "file+datetree" insertmode) ""
|
||
(format " \"%s\"" (if (string= "" title) name title))))
|
||
(format " (file \"~/org/capture/%s\"))" sourceorg)))) )
|
||
table "\n "))
|
||
#+END_SRC
|
||
|
||
#+RESULTS[ec10a65e789d0f3d318de419a7c08e1f41dcb65a]: org-capture-shortcut-gen
|
||
#+begin_example
|
||
'(("e" "Email")
|
||
("ew" "Write Email" entry
|
||
(file+headline org-default-notes-file "Emails")
|
||
(file "~/org/capture/emails.orgcaptmpl"))
|
||
("j" "Journal" entry
|
||
(file+datetree org-journal-file)
|
||
(file "~/org/capture/journal.orgcaptmpl"))
|
||
("l" "Link")
|
||
("ll" "General" entry
|
||
(file+headline org-default-notes-file "General")
|
||
(file "~/org/capture/link.orgcaptmpl"))
|
||
("ly" "YouTube" entry
|
||
(file+headline org-default-notes-file "YouTube")
|
||
(file "~/org/capture/youtube.orgcaptmpl"))
|
||
("L" "Protocol Link" entry
|
||
(file+headline org-default-notes-file "Link")
|
||
(file "~/org/capture/protocol-link.orgcaptmpl"))
|
||
("n" "Notes")
|
||
("nc" "Conlanging" entry
|
||
(file+headline org-conlanging-file "Note")
|
||
(file "~/org/capture/notes.orgcaptmpl"))
|
||
("nn" "General" entry
|
||
(file+headline org-default-notes-file "General")
|
||
(file "~/org/capture/notes.orgcaptmpl"))
|
||
("nN" "Novel" entry
|
||
(file+headline org-novel-notes-file "Note")
|
||
(file "~/org/capture/notes.orgcaptmpl"))
|
||
("nq" "Quote" entry
|
||
(file+headline org-default-notes-file "Quote")
|
||
(file "~/org/capture/notes-quote.orgcaptmpl"))
|
||
("nw" "Worldbuilding" entry
|
||
(file+headline org-wordbuilding-file "Note")
|
||
(file "~/org/capture/notes.orgcaptmpl"))
|
||
("N" "Novel")
|
||
("Ni" "Ideas" entry
|
||
(file+headline org-novel-notes-file "Ideas")
|
||
(file "~/org/capture/notes.orgcaptmpl"))
|
||
("p" "Protocol" entry
|
||
(file+headline org-default-notes-file "Link")
|
||
(file "~/org/capture/protocol.orgcaptmpl"))
|
||
("r" "Resources")
|
||
("rc" "Conlanging" entry
|
||
(file+headline org-conlanging-file "Resources")
|
||
(file "~/org/capture/resource.orgcaptmpl"))
|
||
("re" "Emacs" entry
|
||
(file+headline org-default-notes-file "Emacs")
|
||
(file "~/org/capture/resource.orgcaptmpl"))
|
||
("ri" "Informatique" entry
|
||
(file+headline org-default-notes-file "Informatique")
|
||
(file "~/org/capture/resource.orgcaptmpl"))
|
||
("rl" "Linguistics" entry
|
||
(file+headline org-default-notes-file "Linguistics")
|
||
(file "~/org/capture/resource.orgcaptmpl"))
|
||
("rL" "Linux" entry
|
||
(file+headline org-default-notes-file "Linux")
|
||
(file "~/org/capture/resource.orgcaptmpl"))
|
||
("rw" "Worldbuilding" entry
|
||
(file+headline org-wordbuilding-file "Resources")
|
||
(file "~/org/capture/resource.orgcaptmpl"))
|
||
("t" "Tasks")
|
||
("tb" "Birthday" entry
|
||
(file+headline org-private-agenda-file "Birthday")
|
||
(file "~/org/capture/birthday.orgcaptmpl"))
|
||
("te" "Event" entry
|
||
(file+headline org-private-agenda-file "Event")
|
||
(file "~/org/capture/event.orgcaptmpl"))
|
||
("th" "Health" entry
|
||
(file+headline org-private-agenda-file "Health")
|
||
(file "~/org/capture/health.orgcaptmpl"))
|
||
("ti" "Informatique" entry
|
||
(file+headline org-private-agenda-file "Informatique")
|
||
(file "~/org/capture/informatique.orgcaptmpl")))
|
||
#+end_example
|
||
|
||
Below you can find the equivalent code as described above.
|
||
#+NAME: org-capture-templates
|
||
#+BEGIN_SRC emacs-lisp :noweb yes
|
||
(setq
|
||
org-capture-templates
|
||
<<org-capture-shortcut-gen()>>)
|
||
#+END_SRC
|
||
|
||
You may notice a capture entry for my journal, and this is due to the fact I do
|
||
not use ~org-journal~ anymore: it was too overpowered for me, and I prefer to
|
||
keep it simple with a single file. And as you can see, and unlike a lot of other
|
||
Emacs configurations, the content of the template is not set in the variable,
|
||
but in external files which can be modified freely as actual Org buffers instead
|
||
of trying to get a proper one with loads of ~\n~ characters and such. All these
|
||
templates are declared below.
|
||
|
||
In the next sub-sections will be described my org capture templates. These are
|
||
not tangled into my Emacs configuration files, but into separate ~.orgcaptmpl~
|
||
files stored into =~/org/capture/=.
|
||
|
||
***** Emails
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Org-mode-Org_capture-Emails-d87336fe
|
||
:END:
|
||
This is my template for a new Email:
|
||
#+BEGIN_SRC org :tangle ~/org/capture/email.orgcaptmpl
|
||
,** TODO [#A] Write Email
|
||
SCHEDULED: %^t
|
||
:PROPERTIES:
|
||
:CAPTURED: %U
|
||
:END:
|
||
From: Lucien Cartier-Tilet <lucien@phundrak.com>
|
||
To: %^{Recipient}
|
||
Subject: %^{Object}
|
||
--text follows this line--
|
||
%?
|
||
--
|
||
Lucien “Phundrak” Cartier-Tilet
|
||
https://phundrak.com (Français)
|
||
https://en.phundrak.com (English)
|
||
|
||
Sent from a Free and Open-Source Linux operating system with GNU/Emacs
|
||
#+END_SRC
|
||
|
||
I use it in case my computer is not yet connected to the internet and I need to
|
||
already write the email so I can send it later. All I will need to to afterwards
|
||
will be to copy and paste my capture in a new message buffer and send it once I
|
||
am back online. This is exported to =~/org/capture/email.orgcaptmpl=.
|
||
|
||
***** Journal
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Org-mode-Org_capture-Journal-9916f9bf
|
||
:END:
|
||
This template is quite simple: it creates a new entry with the current timestamp
|
||
as its title, a brief title of my choosing, and then I can write whatever I wish
|
||
to write. This is exported to =~/org/capture/journal.orgcaptmpl=.
|
||
#+BEGIN_SRC org :tangle ~/org/capture/journal.orgcaptmpl
|
||
,* %U %^{Title}
|
||
%?
|
||
#+END_SRC
|
||
|
||
***** Notes
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Org-mode-Org_capture-Notes-4b4c10aa
|
||
:END:
|
||
This template is used for taking note about various subjects that can go from
|
||
conlanging to development. I wrote it so I can know from where this capture was
|
||
made and when, and it even supports text that was highlighted in Emacs that will
|
||
be inserted in a quote block. This is exported to
|
||
=~/org/capture/notes.orgcaptmpl=.
|
||
#+BEGIN_SRC org :tangle ~/org/capture/notes.orgcaptmpl
|
||
,* %^{Title}
|
||
:PROPERTIES:
|
||
:CAPTURED: %U
|
||
:END:
|
||
%?
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC org :tangle ~/org/capture/notes-quote.orgcaptmpl
|
||
,* %^{Title}
|
||
:PROPERTIES:
|
||
:CAPTURED: %U
|
||
:END:
|
||
Possible inspiration:
|
||
,#+begin_quote
|
||
%i
|
||
,#+end_quote
|
||
|
||
%?
|
||
#+END_SRC
|
||
|
||
***** Protocol
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Org-mode-Org_capture-Protocol-ec45ec49
|
||
:END:
|
||
This capture is used when received through org-protocol, with the Org-protocol
|
||
Extension for Firefox. It allows me to save in a quote block what I’ve
|
||
highlighted, as well as the link of the webpage on which my saved content was
|
||
highlighted. This file is exported to =~/org/capture/protocol.orgcaptmpl=.
|
||
#+BEGIN_SRC org :tangle ~/org/capture/protocol.orgcaptmpl
|
||
,* TODO [#C] %^{Title}
|
||
:PROPERTIES:
|
||
:CAPTURED: %U
|
||
:LINK: %:link
|
||
:TITLE: %:description
|
||
:END:
|
||
,#+begin_quote
|
||
%i
|
||
,#+end_quote
|
||
|
||
%?
|
||
#+END_SRC
|
||
|
||
This next capture template is used only when a link is sent to Emacs and no
|
||
content was highlighted.
|
||
#+BEGIN_SRC org :tangle ~/org/capture/protocol-link.orgcaptmpl
|
||
,* TODO [#C] Link: %^{Title}
|
||
:PROPERTIES:
|
||
:CAPTURED: %U
|
||
:LINK: %:link
|
||
:TITLE: %:description
|
||
:END:
|
||
%?
|
||
#+END_SRC
|
||
|
||
***** Resources
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Org-mode-Org_capture-Resources-b23bfbd0
|
||
:END:
|
||
This is the default template for resources, which generally are located on the
|
||
Internet. By default, I give them the lowest priority, because although this is
|
||
something for me to remember later, it is not by default important. You can see
|
||
in the properties I record when the capture happened, and what the link is. The
|
||
title of the capture is a summary of what this is, while the body of the capture
|
||
is a more detailed explanation of what I capture, why, and how it could be
|
||
useful to me.
|
||
#+BEGIN_SRC org :tangle ~/org/capture/resource.orgcaptmpl
|
||
,* TODO [#C] %^{Title}
|
||
:PROPERTIES:
|
||
:CAPTURED: %U
|
||
:LINK: %^{Link}
|
||
:END:
|
||
%?
|
||
#+END_SRC
|
||
|
||
***** Tasks
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Org-mode-Org_capture-Tasks-3fcf382a
|
||
:END:
|
||
****** Computers and stuff
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Org-mode-Org_capture-Tasks-Computers_and_stuff-a4eef8e3
|
||
:END:
|
||
One type of task I often capture is related to my servers or thing about
|
||
computers in general. With this, I can capture a task for which I will either
|
||
set a schedule or a deadline.
|
||
#+BEGIN_SRC org :tangle ~/org/capture/informatique.orgcaptmpl
|
||
,* TODO %^{Title}
|
||
%^{Scheduled or Deadline?||SCHEDULED||DEADLINE}: %^t
|
||
:PROPERTIES:
|
||
:CATEGORY: %^{Category}
|
||
:END:
|
||
%?
|
||
#+END_SRC
|
||
|
||
****** Health
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Org-mode-Org_capture-Tasks-Health-74f8f338
|
||
:END:
|
||
This capture is rarely used (I’m lucky to have a good health), but it can be
|
||
useful.
|
||
#+BEGIN_SRC org :tangle ~/org/capture/health.orgcaptmpl
|
||
,* %^{Title}
|
||
SCHEDULED: %^t
|
||
|
||
%?
|
||
#+END_SRC
|
||
|
||
****** Birthdays
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Org-mode-Org_capture-Tasks-Birthdays-ec3b27be
|
||
:END:
|
||
This capture is used to store new birthdays I have to remember. They are set to
|
||
be repeated yearly.
|
||
#+BEGIN_SRC org :tangle ~/org/capture/birthday.orgcaptmpl
|
||
,* %^{Name}
|
||
SCHEDULED: %^t
|
||
#+END_SRC
|
||
|
||
****** Events
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Org-mode-Org_capture-Tasks-Events-7f0f8dee
|
||
:END:
|
||
#+BEGIN_SRC org :tangle ~/org/capture/event.orgcaptmpl
|
||
,* %^{Title}
|
||
%^{Scheduled or deadline?||SCHEDULED||DEADLINE}: %^t
|
||
%?
|
||
#+END_SRC
|
||
|
||
***** Links
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Org-mode-Org_capture-Links-586a6b2a
|
||
:END:
|
||
****** General
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Org-mode-Org_capture-Links-General-1f0732db
|
||
:END:
|
||
#+BEGIN_SRC org :tangle ~/org/capture/link.orgcaptmpl
|
||
,* TODO [#C] %^{Title}
|
||
:PROPERTIES:
|
||
:CAPTURED: %U
|
||
:LINK: %^{Link}
|
||
:END:
|
||
%?
|
||
#+END_SRC
|
||
|
||
****** YouTube
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Org-mode-Org_capture-Links-YouTube-b89fe20e
|
||
:END:
|
||
#+BEGIN_SRC org :tangle ~/org/capture/youtube.orgcaptmpl
|
||
,* TODO [#C] %^{Title}
|
||
:PROPERTIES:
|
||
:CAPTURED: %U
|
||
:AUTHOR: %^{Author}
|
||
:LINK: %^{Link}
|
||
:END:
|
||
%?
|
||
#+END_SRC
|
||
|
||
**** Custom org-mode functions
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Org-mode-Custom_org-mode_functions-f1726995
|
||
:END:
|
||
We begin with a couple of custom functions that I use in my org-mode files.
|
||
|
||
***** Custom and unique headings ID
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Org-mode-Custom_org-mode_functions-Custom_and_unique_headings_ID-44d2beaf
|
||
:END:
|
||
The first ones are dedicated to provide org-mode headings a fixed and unique ID
|
||
that won’t change over time. This code was taken from
|
||
[[https://writequit.org/articles/emacs-org-mode-generate-ids.html][https://writequit.org/articles/emacs-org-mode-generate-ids.html]]. The first
|
||
function’s job is to create these unique IDs
|
||
#+NAME: org-better-id-new
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun eos/org-id-new (&optional prefix)
|
||
"Create a new globally unique ID.
|
||
|
||
An ID consists of two parts separated by a colon:
|
||
- a prefix
|
||
- a unique part that will be created according to
|
||
`org-id-method'.
|
||
|
||
PREFIX can specify the prefix, the default is given by the
|
||
variable `org-id-prefix'. However, if PREFIX is the symbol
|
||
`none', don't use any prefix even if `org-id-prefix' specifies
|
||
one.
|
||
|
||
So a typical ID could look like \"Org-4nd91V40HI\"."
|
||
(let* ((prefix (if (eq prefix 'none)
|
||
""
|
||
(concat (or prefix org-id-prefix)
|
||
"-"))) unique)
|
||
(when (equal prefix "-")
|
||
(setq prefix ""))
|
||
(cond
|
||
((memq org-id-method
|
||
'(uuidgen uuid))
|
||
(setq unique (org-trim (shell-command-to-string org-id-uuid-program)))
|
||
(unless (org-uuidgen-p unique)
|
||
(setq unique (org-id-uuid))))
|
||
((eq org-id-method 'org)
|
||
(let* ((etime (org-reverse-string (org-id-time-to-b36)))
|
||
(postfix (when org-id-include-domain
|
||
(progn
|
||
(require 'message)
|
||
(concat "@"
|
||
(message-make-fqdn))))))
|
||
(setq unique (concat etime postfix))))
|
||
(t (error "Invalid `org-id-method'")))
|
||
(concat prefix (car (split-string unique "-")))))
|
||
#+END_SRC
|
||
|
||
Now, let’s see the function that will be used to get the custom id of a heading
|
||
at point. If the function does not detect any custom ID, then one should be
|
||
created and inserted.
|
||
#+NAME: org-better-id-get
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun eos/org-custom-id-get (&optional pom create prefix)
|
||
"Get the CUSTOM_ID property of the entry at point-or-marker POM.
|
||
|
||
If POM is nil, refer to the entry at point. If the entry does not
|
||
have an CUSTOM_ID, the function returns nil. However, when CREATE
|
||
is non nil, create a CUSTOM_ID if none is present already. PREFIX
|
||
will be passed through to `eos/org-id-new'. In any case, the
|
||
CUSTOM_ID of the entry is returned."
|
||
(interactive)
|
||
(org-with-point-at pom
|
||
(let* ((orgpath (mapconcat #'identity (org-get-outline-path) "-"))
|
||
(heading (replace-regexp-in-string
|
||
"[_-]+$" ""
|
||
(replace-regexp-in-string
|
||
"[-_]+" "-"
|
||
(replace-regexp-in-string
|
||
"[^a-zA-Z0-9-_]" "-"
|
||
(if (string= orgpath "")
|
||
(org-get-heading t t t t)
|
||
(concat orgpath "_" (org-get-heading t t t t)))))))
|
||
(id (org-entry-get nil "CUSTOM_ID")))
|
||
(cond
|
||
((and id
|
||
(stringp id)
|
||
(string-match "\\S-" id)) id)
|
||
(create (setq id (eos/org-id-new (concat prefix heading)))
|
||
(org-entry-put pom "CUSTOM_ID" id)
|
||
(org-id-add-location id
|
||
(buffer-file-name (buffer-base-buffer)))
|
||
id)))))
|
||
#+END_SRC
|
||
|
||
Finally, this is the function that gets called on file saves. If the function
|
||
detects ~auto-id:t~ among the org options in the ~#+OPTIONS:~ header, then the
|
||
above function is called.
|
||
#+NAME: org-better-id-add-ids
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun eos/org-add-ids-to-headlines-in-file ()
|
||
"Add CUSTOM_ID properties to all headlines in the current file
|
||
which do not already have one.
|
||
|
||
Only adds ids if the `auto-id' option is set to `t' in the file
|
||
somewhere. ie, #+OPTIONS: auto-id:t"
|
||
(interactive)
|
||
(save-excursion
|
||
(widen)
|
||
(goto-char (point-min))
|
||
(when (re-search-forward "^#\\+OPTIONS:.*auto-id:t" (point-max) t)
|
||
(org-map-entries (lambda () (eos/org-custom-id-get (point) 'create))))))
|
||
#+END_SRC
|
||
|
||
Let’s add a hook to the above function so it is called automatically on save,
|
||
and only in read-write functions.
|
||
#+NAME: org-better-id-hooks
|
||
#+BEGIN_SRC emacs-lisp
|
||
(add-hook 'org-mode-hook
|
||
(lambda ()
|
||
(add-hook 'before-save-hook
|
||
(lambda ()
|
||
(when (and (eq major-mode 'org-mode)
|
||
(eq buffer-read-only nil))
|
||
(eos/org-add-ids-to-headlines-in-file))))))
|
||
#+END_SRC
|
||
|
||
**** File export
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Org-mode-Org_files_exports-1e194169
|
||
:END:
|
||
I want to disable by default behavior of ~^~ and ~_~ for only one character,
|
||
making it compulsory to use instead ~^{}~ and ~_{}~ respectively. This is due to
|
||
my frequent usage of the underscore in my org files as a regular character and
|
||
not a markup one, especially when describing phonetics evolution. So, let’s
|
||
disable it:
|
||
#+NAME: org-use-sub-superscripts
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-use-sub-superscripts (quote {}))
|
||
#+END_SRC
|
||
|
||
***** LaTeX
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Emacs-builtins-Org-mode-File-export-LaTeX-92bbd6f3
|
||
:END:
|
||
When it comes to exports, I want the LaTeX and PDF exports to be done with
|
||
XeLaTeX only. This implies the modification of the following variable:
|
||
#+NAME: org-latex-compiler
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-latex-compiler "xelatex")
|
||
#+END_SRC
|
||
|
||
I also want to get by default ~minted~ for LaTeX listings so I can have syntax
|
||
highlights:
|
||
#+NAME: org-latex-listings
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-latex-listings 'minted)
|
||
#+END_SRC
|
||
|
||
The default packages break my LaTeX exports: for some reasons, images are not
|
||
loaded and exported in PDFs, so I needed to redifine the default packages
|
||
excluding the one that broke my exports. I also added two default packages,
|
||
~minted~ and ~xeCJK~ for syntax highlighting and Japanese (and additionally
|
||
Chinese and Korean) support.
|
||
#+NAME: org-latex-default-packages
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-latex-default-packages-alist '(("" "graphicx" t)
|
||
("T1" "fontspec" t ("pdflatex"))
|
||
("" "longtable" nil)
|
||
("" "wrapfig" nil)
|
||
("" "rotating" nil)
|
||
("normalem" "ulem" t)
|
||
("" "amsmath" t)
|
||
("" "textcomp" t)
|
||
("" "amssymb" t)
|
||
("" "capt-of" nil)
|
||
("" "minted" nil)
|
||
("" "hyperref" nil)))
|
||
#+END_SRC
|
||
|
||
By the way, reference links in LaTeX should be written in this format:
|
||
#+NAME: org-export-latex-hyperref-format
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-export-latex-hyperref-format "\\ref{%s}")
|
||
#+END_SRC
|
||
|
||
When it comes to the export itself, the latex file needs to be processed several
|
||
times through XeLaTeX in order to get some references right. Don’t forget to
|
||
also run bibtex!
|
||
#+NAME: org-latex-pdf-process
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-latex-pdf-process
|
||
'("xelatex -8bit -shell-escape -interaction nonstopmode -output-directory %o %f"
|
||
"bibtex %b"
|
||
"xelatex -8bit -shell-escape -interaction nonstopmode -output-directory %o %f"
|
||
"xelatex -8bit -shell-escape -interaction nonstopmode -output-directory %o %f"))
|
||
#+END_SRC
|
||
|
||
***** HTML
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Emacs-builtins-Org-mode-File-export-HTML-192cba41
|
||
:END:
|
||
For Reveal.JS exports, I need to set where to find the framework by default:
|
||
#+NAME: org-re-reveal-root
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-re-reveal-root "https://cdn.jsdelivr.net/npm/reveal.js")
|
||
#+END_SRC
|
||
|
||
On HTML exports, Org-mode tries to include a validation link for the exported
|
||
HTML. Let’s disable that since I never use it.
|
||
#+NAME: org-html-validation
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq org-html-validation-link nil)
|
||
#+END_SRC
|
||
|
||
However, something I very often use are metadata information in my HTML files. I
|
||
just want to automate that. Fortunately, [[https://tecosaur.github.io/emacs-config/config.html#extra-header-content,code--3][Tecosaur comes to the rescue]]! First,
|
||
here is our function that will generate all the meta tags in our HTML:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun org-html-meta-tags-fancy (info)
|
||
"Use the INFO plist to construct the meta tags, as described in
|
||
`org-html-meta-tags'."
|
||
(message "%s" info)
|
||
(let ((title (org-html-plain-text
|
||
(org-element-interpret-data (plist-get info :title)) info))
|
||
(author (and (plist-get info :with-author)
|
||
(let ((auth (plist-get info :author)))
|
||
;; Return raw Org syntax.
|
||
(and auth (org-html-plain-text
|
||
(org-element-interpret-data auth) info))))))
|
||
(list
|
||
(when (org-string-nw-p author)
|
||
(list "name" "author" author))
|
||
(when (org-string-nw-p (plist-get info :description))
|
||
(list "name" "description"
|
||
(plist-get info :description)))
|
||
'("name" "generator" "org mode (Emacs)")
|
||
'("name" "theme-color" "#3b4252")
|
||
'("property" "og:type" "article")
|
||
(list "property" "og:title" title)
|
||
(let ((subtitle (org-export-data (plist-get info :subtitle) info)))
|
||
(when (org-string-nw-p subtitle)
|
||
(list "property" "og:description" subtitle)))
|
||
(let ((meta-image (org-export-data (plist-get info :metaimage) info)))
|
||
(when (org-string-nw-p meta-image)
|
||
(list "property" "og:image" meta-image)))
|
||
(let ((meta-image-type (org-export-data (plist-get info :metaimagetype) info)))
|
||
(when (org-string-nw-p meta-image-type)
|
||
(list "property" "og:image:type" meta-image-type)))
|
||
(let ((meta-image-width (org-export-data (plist-get info :metaimagewidth) info)))
|
||
(when (org-string-nw-p meta-image-width)
|
||
(list "property" "og:image:width" meta-image-width)))
|
||
(let ((meta-image-height (org-export-data (plist-get info :metaimageheight) info)))
|
||
(when (org-string-nw-p meta-image-height)
|
||
(list "property" "og:image:height" meta-image-height)))
|
||
(let ((meta-image-alt (org-export-data (plist-get info :metaimagealt) info)))
|
||
(when (org-string-nw-p meta-image-alt)
|
||
(list "property" "og:image:alt" meta-image-alt)))
|
||
(when (org-string-nw-p author)
|
||
(list "property" "og:article:author:first_name" (car (s-split-up-to " " author 2))))
|
||
(when (and (org-string-nw-p author) (s-contains-p " " author))
|
||
(list "property" "og:article:author:last_name" (cadr (s-split-up-to " " author 2))))
|
||
(list "property" "og:article:published_time" (format-time-string "%FT%T%z")))))
|
||
#+END_SRC
|
||
|
||
This will use some special keywords in our org buffer and insert their content in
|
||
|
||
Now let’s bind it to when we export our org buffer to HTML:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(unless (functionp #'org-html-meta-tags-default)
|
||
(defalias 'org-html-meta-tags-default #'ignore))
|
||
(setq org-html-meta-tags #'org-html-meta-tags-fancy)
|
||
#+END_SRC
|
||
|
||
**** LaTeX formats
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Org-mode-Custom_LaTeX_formats-8e8dca1c
|
||
:END:
|
||
I currently have two custom formats for my Org-mode exports: one for general use
|
||
(initialy for my conlanging files, hence its ~conlang~ name), and one for beamer
|
||
exports.
|
||
|
||
Below is the declaration of the ~conlang~ LaTeX class:
|
||
#+NAME: org-latex-class-conlang
|
||
#+BEGIN_SRC emacs-lisp
|
||
'("conlang"
|
||
"\\documentclass{book}"
|
||
("\\chapter{%s}" . "\\chapter*{%s}")
|
||
("\\section{%s}" . "\\section*{%s}")
|
||
("\\subsection{%s}" . "\\subsection*{%s}")
|
||
("\\subsubsection{%s}" . "\\subsubsection*{%s}"))
|
||
#+END_SRC
|
||
|
||
And here is the declaration of the ~beamer~ class:
|
||
#+NAME: org-latex-class-beamer
|
||
#+BEGIN_SRC emacs-lisp
|
||
`("beamer"
|
||
,(concat "\\documentclass[presentation]{beamer}\n"
|
||
"[DEFAULT-PACKAGES]"
|
||
"[PACKAGES]"
|
||
"[EXTRA]\n")
|
||
("\\section{%s}" . "\\section*{%s}")
|
||
("\\subsection{%s}" . "\\subsection*{%s}")
|
||
("\\subsubsection{%s}" . "\\subsubsection*{%s}"))
|
||
#+END_SRC
|
||
|
||
Both these classes have to be added to ~org-latex-classes~ like so:
|
||
#+NAME: org-latex-classes
|
||
#+BEGIN_SRC emacs-lisp :noweb yes
|
||
(eval-after-load "ox-latex"
|
||
'(progn
|
||
(add-to-list 'org-latex-classes
|
||
<<org-latex-class-conlang>>
|
||
)
|
||
(add-to-list 'org-latex-classes
|
||
<<org-latex-class-beamer>>
|
||
)))
|
||
#+END_SRC
|
||
|
||
**** Projects
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Org-mode-Org_projects-5be088cd
|
||
:END:
|
||
Another great features of Org-mode is the Org projects that allow the user to
|
||
easily publish a bunch of org files to a remote location. Here is the current
|
||
declaration of my projects, which will be detailed later:
|
||
#+NAME: org-publish-projects
|
||
#+BEGIN_SRC emacs-lisp :noweb yes
|
||
<<org-proj-config-setup>>
|
||
<<org-proj-lang-setup>>
|
||
(setq org-publish-project-alist
|
||
`(
|
||
<<org-proj-config-html>>
|
||
<<org-proj-config-static>>
|
||
<<org-proj-config>>
|
||
<<org-proj-lang-html>>
|
||
<<org-proj-lang-pdf>>
|
||
<<org-proj-lang-static>>
|
||
<<org-proj-lang>>))
|
||
#+END_SRC
|
||
|
||
***** Configuration website
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Org-mode-Org_projects-Configuration_website-79bd0468
|
||
:END:
|
||
This is my configuration for exporting my dotfiles to my website in a web format
|
||
only. No PDFs or anything, just HTML. Please note that I do not use that often
|
||
anymore, I much prefer the automatic script that I have which deploys through my
|
||
Drone instance my website on git pushes.
|
||
|
||
And before we get into the actual configuration, I would like to introduce a
|
||
couple of variables. This is a bit more verbose than if I declared everything
|
||
manually, but now I can change all three values at the same time without a
|
||
hasle.
|
||
#+NAME: org-proj-config-setup
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defvar phundrak//projects-config-target
|
||
"/rsync:Tilo:~/www/phundrak.com/config"
|
||
"Points to where exported files for config.phundrak.com should be put")
|
||
(defvar phundrak//projects-config-source
|
||
"~/org/config/"
|
||
"Points to where the sources for config.phundrak.com are")
|
||
(defvar phundrak//projects-config-language
|
||
"en"
|
||
"Language of config.phundrak.com")
|
||
(defvar phundrak//projects-config-recursive
|
||
t
|
||
"Defines whether subdirectories should be parsed for config.phundrak.com")
|
||
#+END_SRC
|
||
|
||
Now, here is my configuration. In this snippet, my org files located in my
|
||
source directory get exported in the HTML format and published to my target
|
||
directory on my remote server through RSYNC via TRAMP. A sitemap is
|
||
automatically generated, which comes in handy with the online sitemap that is
|
||
available through the navigation bar.
|
||
#+NAME: org-proj-config-html
|
||
#+BEGIN_SRC emacs-lisp
|
||
("config-website-org"
|
||
:base-directory ,phundrak//projects-config-source
|
||
:base-extension "org"
|
||
:publishing-directory ,phundrak//projects-config-target
|
||
:recursive ,phundrak//projects-config-recursive
|
||
:language ,phundrak//projects-config-language
|
||
:publishing-function org-html-publish-to-html
|
||
:headline-levels 5
|
||
:auto-sitemap t
|
||
:auto-preamble t)
|
||
#+END_SRC
|
||
|
||
We also have the component for all the static files needed to run the website
|
||
(mostly images tbh).
|
||
#+NAME: org-proj-config-static
|
||
#+BEGIN_SRC emacs-lisp
|
||
("config-website-static"
|
||
:base-directory ,phundrak//projects-config-source
|
||
:base-extension "png\\|jpg\\|gif\\|webp\\|svg\\|jpeg\\|ttf\\|woff\\|txt\\|epub\\|md"
|
||
:publishing-directory ,phundrak//projects-config-target
|
||
:recursive ,phundrak//projects-config-recursive
|
||
:language ,phundrak//projects-config-language
|
||
:publishing-function org-publish-attachment)
|
||
#+END_SRC
|
||
|
||
The project is then defined like so:
|
||
#+NAME: org-proj-config
|
||
#+BEGIN_SRC emacs-lisp
|
||
("config-website"
|
||
:components ("config-website-org"
|
||
"config-website-static"))
|
||
#+END_SRC
|
||
|
||
***** Linguistics website
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Org-mode-Org_projects-Linguistics_website-34b8d4e7
|
||
:END:
|
||
My linguistics website is made out of three projects. As for the previous
|
||
project, let’s declare the common values for these.
|
||
#+NAME: org-proj-lang-setup
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defvar phundrak//projects-conlanging-target
|
||
"/rsync:Tilo:~/www/phundrak.com/langue/"
|
||
"Points to where exported files for langue.phundrak.com should be put")
|
||
(defvar phundrak//projects-conlanging-source
|
||
"~/Documents/conlanging/content/"
|
||
"Points to where the sources for langue.phundrak.com are")
|
||
(defvar phundrak//projects-conlanging-language
|
||
"fr"
|
||
"Language of langue.phundrak.com")
|
||
(defvar phundrak//projects-conlanging-recursive
|
||
t
|
||
"Defines whether subdirectories should be parsed for langue.phundrak.com")
|
||
#+END_SRC
|
||
|
||
The first component is the one generating the HTML files from the org files.
|
||
#+NAME: org-proj-lang-html
|
||
#+BEGIN_SRC emacs-lisp
|
||
("langue-phundrak-com-org"
|
||
:base-directory ,phundrak//projects-conlanging-source
|
||
:base-extension "org"
|
||
:exclude "\\./\\(CONTRIB\\|README\\|head\\|temp\\|svg-ink\\).*"
|
||
:publishing-directory ,phundrak//projects-conlanging-target
|
||
:recursive ,phundrak//projects-conlanging-recursive
|
||
:language ,phundrak//projects-conlanging-language
|
||
:publishing-function org-html-publish-to-html
|
||
:headline-levels 5
|
||
:auto-sitemap t
|
||
:auto-preamble t)
|
||
#+END_SRC
|
||
|
||
We also have the component for the LaTeX and PDF part of the website:
|
||
#+NAME: org-proj-lang-pdf
|
||
#+BEGIN_SRC emacs-lisp
|
||
("langue-phundrak-com-pdf"
|
||
:base-directory ,phundrak//projects-conlanging-source
|
||
:base-extension "org"
|
||
:exclude "\\./\\(CONTRIB\\|README\\|index\\|head\\|temp\\|svg-ink\\).*"
|
||
:publishing-directory ,phundrak//projects-conlanging-target
|
||
:recursive ,phundrak//projects-conlanging-recursive
|
||
:language ,phundrak//projects-conlanging-language
|
||
:publishing-function org-latex-publish-to-pdf
|
||
:headline-levels 5
|
||
:auto-preamble t)
|
||
#+END_SRC
|
||
|
||
And lastly, we have the component for all the static files needed to run the
|
||
website:
|
||
#+NAME: org-proj-lang-static
|
||
#+BEGIN_SRC emacs-lisp
|
||
("langue-phundrak-com-static"
|
||
:base-directory ,phundrak//projects-conlanging-source
|
||
:base-extension "png\\|jpg\\|gif\\|webp\\|svg\\|jpeg\\|ttf\\|woff\\|txt\\|epub"
|
||
:publishing-directory ,phundrak//projects-conlanging-target
|
||
:recursive ,phundrak//projects-conlanging-recursive
|
||
:language ,phundrak//projects-conlanging-language
|
||
:publishing-function org-publish-attachment)
|
||
#+END_SRC
|
||
|
||
The project is then defined like so:
|
||
#+NAME: org-proj-lang
|
||
#+BEGIN_SRC emacs-lisp
|
||
("langue-phundrak-com"
|
||
:components ("langue-phundrak-com-org"
|
||
"langue-phundrak-com-static"
|
||
"langue-phundrak-com-pdf"))
|
||
#+END_SRC
|
||
|
||
**** User information
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Org-mode-Org_variables-User_information-6c7d5e3f
|
||
:END:
|
||
Some variables about myself need to be set so Org-mode knows what information to
|
||
include in exported files.
|
||
#+NAME: org-user-information
|
||
#+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
|
||
|
||
*** Recentf
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Emacs-builtins-Recentf-b00d8ddd
|
||
:END:
|
||
~recentf-mode~ allows Emacs to list all recent files it read. It is also used by
|
||
Spacemacs to display a list of recent files so they can be quickly opened by the
|
||
user. Unfortunately, a lot of these files are just noise I don’t care about, but
|
||
fortunately we can ignore files with the variable ~recentf-exclude~. So, I will
|
||
ignore these paths:
|
||
#+name: recentf-ignored-paths
|
||
| =~/.mail/= |
|
||
| =~/.emacs.d/= |
|
||
| =~/.config/emacs/= |
|
||
| =~/.elfeed/index= |
|
||
| =/tmp/= |
|
||
|
||
#+name: recentf-ignored-paths-gen
|
||
#+header: :var paths=recentf-ignored-paths
|
||
#+BEGIN_SRC emacs-lisp :tangle no :exports none
|
||
(mapconcat (lambda (path)
|
||
(format "(add-to-list 'recentf-exclude\n (expand-file-name \"%s\"))"
|
||
(replace-regexp-in-string "=" "" (car path))))
|
||
paths
|
||
"\n")
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC emacs-lisp :noweb yes
|
||
(with-eval-after-load 'recentf
|
||
<<recentf-ignored-paths-gen()>>)
|
||
#+END_SRC
|
||
|
||
** Editing and modes
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Editing_and_modes-7dbaf258
|
||
:END:
|
||
*** Default modes
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Editing_and_modes-Default_modes-50d4e086
|
||
:END:
|
||
Some buffers sometimes won’t have a default mode at all, such as the ~*scratch*~
|
||
buffer. In any vanilla configuration, they will then default to ~text-mode~. I
|
||
personally prefer ~org-mode~ to be my default mode, so let’s set it so!
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq edit-server-default-major-mode 'org-mode)
|
||
#+END_SRC
|
||
|
||
*** Evil
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Editing_and_modes-Evil-3cedaaee
|
||
:END:
|
||
As a user of Evil, I’m sometimes pissed when I accidentally press ~C-u~ and it
|
||
gets me to the top of the document. So, let’s disable it:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq evil-want-C-u-scroll nil)
|
||
#+END_SRC
|
||
|
||
*** File extensions
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-File_extensions-f76fe752
|
||
:END:
|
||
Sometimes, Emacs doesn’t recognize or misrecognizes some extensions, resulting
|
||
in a wrong mode set for said file. Let’s fix that by associating the extension
|
||
with the desired mode:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(dolist (e '(("xml" . web-mode)
|
||
("xinp" . web-mode)
|
||
("aiml" . web-mode)
|
||
("C" . c++-mode)
|
||
("dconf" . conf-mode)
|
||
("yy" . bison-mode)
|
||
("ll" . flex-mode)
|
||
("s" . asm-mode)
|
||
("pl" . prolog-mode)
|
||
("l" . scheme-mode)
|
||
("vs" . glsl-mode)
|
||
("fs" . glsl-mode)))
|
||
(push (cons (concat "\\."
|
||
(car e)
|
||
"\\'") (cdr e))
|
||
auto-mode-alist))
|
||
#+END_SRC
|
||
|
||
We also have a couple of extensions which should all be in ~conf-unix-mode~,
|
||
let’s indicate that to Emacs:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(dolist (e '("service" "timer" "target" "mount" "automount"
|
||
"slice" "socket" "path" "netdev" "network"
|
||
"link"))
|
||
(push (cons (concat "\\." e "\\'") 'conf-unix-mode)
|
||
auto-mode-alist))
|
||
#+END_SRC
|
||
|
||
*** Hooks
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Miscellaneous-Hooks-86da2da0
|
||
:END:
|
||
I also have some hooks I use for enabling some major and minor modes. The first
|
||
one here allows the execution of the deletion of trailing space each time I save
|
||
a file.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(add-hook 'before-save-hook 'delete-trailing-whitespace)
|
||
#+END_SRC
|
||
|
||
I also want to always be in ~visual-line-mode~ so Emacs soft-wraps lines that
|
||
are too long for the buffer they are displayed in. This will also be enabled for
|
||
Elfeed.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(add-hook 'prog-mode-hook 'visual-line-mode)
|
||
(add-hook 'elfeed-read-mode-hook 'visual-line-mode)
|
||
#+END_SRC
|
||
|
||
I also want for some non-programming modes to enable a hard-limit in terms of
|
||
how many characters can fit on one line. The modes that benefit are
|
||
~message-mode~, ~org-mode~, ~text-mode~ and ~markdown-mode~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(mapc (lambda (x)
|
||
(add-hook x 'visual-line-mode))
|
||
'(message-mode-hook
|
||
text-mode-hook
|
||
markdown-mode-hook))
|
||
#+END_SRC
|
||
|
||
*** Twittering mode
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Miscellaneous-Twittering_mode-b97d9327
|
||
:END:
|
||
For ~twittering-mode~, a Twitter major mode for Emacs, I want to encrypt my data
|
||
using a master password, which I do thanks to this option:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq twittering-use-master-password t)
|
||
#+END_SRC
|
||
|
||
*** Wrapping regions
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Editing_and_modes-Wrapping_regions-2250281e
|
||
:END:
|
||
I really like the ~M-(~ keybinding for wrapping a selected region between
|
||
parenthesis. However, parenthesis are not everything (even in Lisp dialects),
|
||
and other wrappers could be nice. And they are! Here is how they are declared:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(global-set-key (kbd "M-[") 'insert-pair)
|
||
(global-set-key (kbd "M-{") 'insert-pair)
|
||
(global-set-key (kbd "M-<") 'insert-pair)
|
||
(global-set-key (kbd "M-'") 'insert-pair)
|
||
(global-set-key (kbd "M-`") 'insert-pair)
|
||
(global-set-key (kbd "M-\"") 'insert-pair)
|
||
#+END_SRC
|
||
|
||
For the record, this is from [[http://www.howardism.org/][Howard Abram]]’s [[https://github.com/howardabrams/dot-files][dotfiles]].
|
||
|
||
** Keybindings
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Shortcuts-aef3f7a7
|
||
:END:
|
||
As you will see, I defined a LOT of custom keybindings. All of them are
|
||
Spacemacs keybindings, defined in a way they can be used seamlessly with Evil.
|
||
They almost all begin with ~o~, which is a prefix reserved for user-defined
|
||
keybindings so they won’t conflict with any package. Let’s declare it like so.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(spacemacs/declare-prefix "o" "custom")
|
||
#+END_SRC
|
||
|
||
Now, all keybindings that will be defined can be invoked in Normal-mode with the
|
||
~SPC~ key followed by the sequence assigned to each keybinding.
|
||
|
||
Before some more specialized categories, I have two commands which don’t fit
|
||
into any other category that I sometime use. The first one is a fix for the Bépo
|
||
keybindings which left out a keybind: ~winum-select-window-by-number~ is still
|
||
bound to ~SPC ²~, which is not a key that is available on the bépo layout
|
||
(instead, we use the dead key ~^~ followed by ~2~, or any digits). So instead,
|
||
let’s use the key that is physically in the same place: ~$~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(spacemacs/declare-prefix "$" "select window by number")
|
||
(spacemacs/set-leader-keys "$" 'winum-select-window-by-number)
|
||
#+END_SRC
|
||
|
||
The following, I use it rarely, it can launch an external command from Emacs to
|
||
launch, for instance, my web browser or any other software not related to Emacs.
|
||
It offers a similar interface to [[https://wiki.archlinux.org/index.php/Dmenu][dmenu]] through helm.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(spacemacs/declare-prefix "or" "external command")
|
||
(spacemacs/set-leader-keys "or" 'helm-run-external-command)
|
||
#+END_SRC
|
||
|
||
However this one I use often, generally in org or text buffers.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(spacemacs/set-leader-keys "os" 'sort-lines)
|
||
#+END_SRC
|
||
|
||
*** Applications
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Shortcuts-Applications-af8730b1
|
||
:END:
|
||
As this is a new category, let’s declare its prefix:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(spacemacs/declare-prefix "oa" "applications")
|
||
#+END_SRC
|
||
|
||
Now, let’s also declare the keybindings in this category. ~oac~ will invoke
|
||
Emacs’ calculator, while ~oac~ invokes the calendar, ~oae~ invokes the Eww web
|
||
browser, ~oaw~ invokes ~woman~ (actually ~helm-man-woman~), and ~oaW~ invokes
|
||
the weather forecast. Lastly, the apostrophe in ~o'~ will invoke Eshell
|
||
directly, without any popup window as with ~,'~ while ~oan~ will open a new
|
||
eshell buffer if another one already exists.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(spacemacs/set-leader-keys
|
||
"o'" 'eshell
|
||
"oac" 'calc
|
||
"oaC" 'calendar
|
||
"oae" 'eww
|
||
"oan" 'eshell-new
|
||
"oaw" 'helm-man-woman
|
||
"oaW" 'wttrin)
|
||
#+END_SRC
|
||
|
||
**** Image mode
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Keybindings-Applications-Image-mode-e5df694e
|
||
:END:
|
||
Viewing images in Emacs is nice, but I want to be able to do more than just view
|
||
them, such as opening them in GIMP. I’ll also declare a couple of keybindings
|
||
that make sense to me.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(spacemacs/declare-prefix-for-mode 'image-mode "G" "Open in GIMP")
|
||
(spacemacs/declare-prefix-for-mode 'image-mode "o" "Open in external viewer")
|
||
(spacemacs/declare-prefix-for-mode 'image-mode "r" "Rotate clockwise")
|
||
(spacemacs/set-leader-keys-for-major-mode 'image-mode
|
||
"G" (lambda () (interactive) (start-process "" nil "gimp" (buffer-name)))
|
||
"o" (lambda () (interactive) (start-process "" nil "xdg-open" (buffer-name)))
|
||
"r" 'image-rotate)
|
||
#+END_SRC
|
||
|
||
**** Org tree slide
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Keybindings-Applications-Org_tree_slide-29545c5e
|
||
:END:
|
||
Finally, here we have the keybindings for ~org-tree-slide~, a presentation mode
|
||
with orgmode. Since I want the keys to be directly accessible without any prefix
|
||
from Spacemacs, I’ll have to declare them the vanilla way. First we have
|
||
keybindings that will launch the presentation:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(define-key org-mode-map (kbd "<f8>") 'org-tree-slide-mode)
|
||
(define-key org-mode-map (kbd "s-<f8>") 'org-tree-slide-skip-done-toggle)
|
||
#+END_SRC
|
||
|
||
Next, we have some additional keybindings that will only be active when in
|
||
~org-tree-slide-mode~. The first one will allow us to exit this mode, while the
|
||
second one will toggle the display of headers marked as ~DONE~. Next, we have
|
||
~F9~ and ~F10~ which are bound to movement in the slide, while ~F11~ changes the
|
||
way the content is displayed. We also set ~org-tree-slide-skip-outline-level~ to
|
||
set the maximum depth we will display as an individual heading during the
|
||
presentation.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(when (require 'org-tree-slide nil t)
|
||
(global-set-key (kbd "<f8>") 'org-tree-slide-mode)
|
||
(global-set-key (kbd "S-<f8>") 'org-tree-slide-skip-done-toggle)
|
||
(define-key org-tree-slide-mode-map (kbd "<f9>")
|
||
'org-tree-slide-move-previous-tree)
|
||
(define-key org-tree-slide-mode-map (kbd "<f10>")
|
||
'org-tree-slide-move-next-tree)
|
||
(define-key org-tree-slide-mode-map (kbd "<f11>")
|
||
'org-tree-slide-content)
|
||
(setq org-tree-slide-skip-outline-level 4)
|
||
(org-tree-slide-narrowing-control-profile)
|
||
(setq org-tree-slide-skip-done nil))
|
||
#+END_SRC
|
||
|
||
*** Comments
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Shortcuts-Comments-508db33d
|
||
:END:
|
||
Some keybindings are also related to comment editing, in particular using
|
||
outorg. Let’s first declare the dedicated prefix:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(spacemacs/declare-prefix "oc" "comments")
|
||
#+END_SRC
|
||
|
||
Now, let’s declare the following keybindings:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(spacemacs/set-leader-keys
|
||
"occ" 'outorg-copy-edits-and-exit
|
||
"oce" 'outorg-edit-as-org
|
||
"oco" 'outline-minor-mode)
|
||
#+END_SRC
|
||
~oco~ enables the outline minor mode, which then allows for the edition of
|
||
comments in org buffers with ~oce~ and saving them to the original source file
|
||
with ~occ~.
|
||
|
||
*** Dired
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Keybindings-Dired-2a45af8d
|
||
:END:
|
||
A couple of keybindings will be added to Dired. The first one is the opening
|
||
parenthesis which will enable or disable ~dired-hide-details-mode~. On the other
|
||
hand, a closing parenthesis will show git information in the current Dired
|
||
buffer.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(define-key dired-mode-map (kbd "(") 'dired-hide-details-mode)
|
||
(define-key dired-mode-map (kbd ")") 'dired-git-info-mode)
|
||
#+END_SRC
|
||
|
||
Something I use from time to time is ~S-F1~ for opening dired in my ~$HOME~
|
||
directory. For that, I simply did the following:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(global-set-key (kbd "<s-f1>") (lambda () (interactive) (dired "~/")))
|
||
#+END_SRC
|
||
|
||
A couple of other useful utilities, sach as opening all marked files, sorting
|
||
files, opening them externally and renaming them, are also bound to a simple key
|
||
press:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(define-key dired-mode-map (kbd "f") 'phundrak-open-marked-files)
|
||
(define-key dired-mode-map (kbd "F") 'xah/open-in-external-app)
|
||
(define-key dired-mode-map (kbd "s") 'xah/dired-sort)
|
||
#+END_SRC
|
||
|
||
*** Files
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Shortcuts-Files-206c2126
|
||
:END:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(spacemacs/declare-prefix "of" "open org file")
|
||
(spacemacs/set-leader-keys "of" 'phundrak-find-org-files)
|
||
#+END_SRC
|
||
|
||
I also have a shortcut for ~helm-locate~ in case I need to find a file that is
|
||
not in these directories. One advantage of this over ~helm-find~ is that it
|
||
doesn’t matter from where I call it, it will find any file on my system that
|
||
matches the query, whereas ~helm-find~ will only search in the current directory
|
||
and its subdirectories. This time, the declaration is much simpler:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(spacemacs/declare-prefix "oF" "locate file")
|
||
(spacemacs/set-leader-keys "oF" 'helm-locate)
|
||
#+END_SRC
|
||
|
||
And that’s it! This should list all my org files under these directories and
|
||
give me fuzzy finding for these files. I just need to partially type the name of
|
||
the file I want to open and it should open without any issue.
|
||
|
||
*** Games
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Keybindings-Games-c9e6ac80
|
||
:END:
|
||
Just to make it easier to launch it, I’ll declare a shortcut for launching
|
||
tetris (which is built into Emacs).
|
||
#+BEGIN_SRC emacs-lisp
|
||
(spacemacs/declare-prefix "oat" "tetris")
|
||
(spacemacs/set-leader-keys "oat" 'tetris)
|
||
#+END_SRC
|
||
|
||
Apparently, no evil keybindings are set for Tetris. Let’s declare them (adapted
|
||
to the bépo layout):
|
||
#+BEGIN_SRC emacs-lisp
|
||
(require 'tetris)
|
||
(define-key tetris-mode-map (kbd "c") 'tetris-move-left)
|
||
(define-key tetris-mode-map (kbd "t") 'tetris-move-down)
|
||
(define-key tetris-mode-map (kbd "s") 'tetris-rotate-prev)
|
||
(define-key tetris-mode-map (kbd "r") 'tetris-move-right)
|
||
#+END_SRC
|
||
|
||
*** Multiple cursors
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Shortcuts-Multiple_cursors-83db7c9c
|
||
:END:
|
||
I don’t really like Spacemacs’ layer for MultipleCursors, so I prefer to simply
|
||
install the package and create shortcuts for it myself. Let’s first declare
|
||
category:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(spacemacs/declare-prefix "om" "multiple-cursors")
|
||
#+END_SRC
|
||
|
||
Now, let’s declare the shortcuts related to multiple-cursors:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(spacemacs/set-leader-keys
|
||
"ome" 'mc/edit-lines
|
||
"omn" 'mc/mark-next-like-this
|
||
"omp" 'mc/mark-previous-like-this
|
||
"oma" 'mc/mark-all-like-this)
|
||
#+END_SRC
|
||
|
||
*** Org-mode
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Shortcuts-Org-mode-a8938199
|
||
:END:
|
||
Now, onto some shortcuts related to org-mode. Let’s first declare the category:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(spacemacs/declare-prefix-for-mode 'org-mode "mo" "custom" "User-defined keybindings")
|
||
(spacemacs/declare-prefix-for-mode 'org-mode "mot" "toggle" "Toggle org elements")
|
||
(spacemacs/declare-prefix-for-mode 'org-mode "moT" "tables")
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(spacemacs/set-leader-keys-for-major-mode 'org-mode "ob" 'phundrak-blog-publish)
|
||
(spacemacs/declare-prefix-for-mode 'org-mode "ob" "publish blog")
|
||
#+END_SRC
|
||
|
||
Now, I have a couple of shortcuts I use regularly:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(spacemacs/set-leader-keys-for-major-mode 'org-mode
|
||
"os" 'org-insert-structure-template
|
||
"ots" 'phundrak/toggle-org-src-window-split
|
||
"ott" 'org-sidebar-tree)
|
||
(spacemacs/declare-prefix-for-mode 'org-mode "moS" "insert template")
|
||
(spacemacs/declare-prefix-for-mode 'org-mode "mots" "toggle src split")
|
||
#+END_SRC
|
||
~os~ allows me to insert an org structure template defined in
|
||
~org-structure-template-alist~ (see [[#User_Configuration-Org-mode-Org_variables-Org_behavior-0319db38][Org behavior]]), while ~ott~ displays the
|
||
outline of the current org file.
|
||
~oT~ is the prefix for tree-related operations:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(spacemacs/declare-prefix-for-mode 'org-mode "moT" "tables")
|
||
#+END_SRC
|
||
|
||
These shortcuts allow to manipulate the width of the column the cursor is
|
||
currently in, by either shrinking it, expanding it, or toggling its state
|
||
between shrunk or expanded. A prefix for all of these commands has been also
|
||
added in order to make the purpose of the shortcuts clearer.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(spacemacs/set-leader-keys-for-major-mode 'org-mode
|
||
"oTt" 'org-table-toggle-column-width
|
||
"oTe" 'org-table-expand
|
||
"oTs" 'org-table-shrink)
|
||
(spacemacs/declare-prefix-for-mode 'org-mode "moTt" "toggle width")
|
||
(spacemacs/declare-prefix-for-mode 'org-mode "moTe" "expand")
|
||
(spacemacs/declare-prefix-for-mode 'org-mode "moTs" "shrink")
|
||
#+END_SRC
|
||
|
||
Finaly, I set the following shortcut in order to easily remove ~RESULTS~ blocks
|
||
from org source code blocks:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(spacemacs/set-leader-keys-for-major-mode 'org-mode
|
||
"or" 'org-babel-remove-result-one-or-many)
|
||
(spacemacs/declare-prefix-for-mode 'org-mode "mor" "remove org result")
|
||
#+END_SRC
|
||
|
||
*** Toggle
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Shortcuts-Toggle-d53c27ef
|
||
:END:
|
||
This category allows to toggle some modes and options.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(spacemacs/declare-prefix "ot" "toggle")
|
||
#+END_SRC
|
||
|
||
As you can see, I have here four shortcuts for toggling various elements in
|
||
Emacs:
|
||
- ~otb~ :: toggles ~fancy-battery-mode~. This comes in very handy when I am on a
|
||
laptop that is not pluged in or which is charging.
|
||
- ~otd~ :: toggles ~elcord-mode~. This mode is used to create an Emacs rich
|
||
integration in Discord.
|
||
- ~otf~ :: toggles the activation of FlyCheck, Emacs’ spell checker. It is by
|
||
default disabled, and I can turn it on with this shortcut only when needed.
|
||
- ~ots~ :: toggles ~prettify-symbols-mode~. This allows Emacs to replace some
|
||
symbols by some others, like for example by replacing ~lambda~ in Emacs Lisp
|
||
buffers with an actual λ.
|
||
- ~otS~ :: toggles whether or not Eshell should shorten the current path in its
|
||
prompt
|
||
#+BEGIN_SRC emacs-lisp
|
||
(spacemacs/set-leader-keys
|
||
"otb" 'fancy-battery-mode
|
||
"otd" 'elcord-mode
|
||
"otf" 'flycheck-mode
|
||
"ots" 'prettify-symbols-mode
|
||
"otS" 'phundrak-prompt-toggle-abbreviation)
|
||
#+END_SRC
|
||
|
||
We also have some input methods-related shortcuts in a sub-category: ~oti~. The
|
||
first shortcuts below are used to either toggle between no input method or the
|
||
last one used (~otit~), or choose an input method among the various available
|
||
ones from Emacs (~otis~).
|
||
#+BEGIN_SRC emacs-lisp
|
||
(spacemacs/declare-prefix "oti" "input methods")
|
||
(spacemacs/set-leader-keys
|
||
"otit" 'toggle-input-method
|
||
"otis" 'set-input-method)
|
||
#+END_SRC
|
||
|
||
The shortcuts below though allow me to directly switch to one of these three
|
||
known input methods I sometimes or often use, namely Japanese, Tibetan and IPA
|
||
(by typing in X-SAMPA).
|
||
#+BEGIN_SRC emacs-lisp
|
||
(spacemacs/declare-prefix "otij" "Japanese")
|
||
(spacemacs/declare-prefix "otix" "IPA (X-SAMPA)")
|
||
(spacemacs/declare-prefix "otiT" "Tibetan")
|
||
(spacemacs/set-leader-keys
|
||
"otij" (lambda () (interactive) (set-input-method 'japanese))
|
||
"otix" (lambda () (interactive) (set-input-method 'ipa-x-sampa))
|
||
"otiT" (lambda () (interactive) (set-input-method 'tibetan-wylie)))
|
||
#+END_SRC
|
||
|
||
** Mu4e
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Mu4e-f3df8e9e
|
||
:END:
|
||
Mu4e is a frontend for mu, an email analyzer which sits on top of a Maildir
|
||
which gets updated with the ~mbsync~ command from ~isync~. It has a lot of neat
|
||
features, but I guess my favorite ones are:
|
||
1. the search query feature
|
||
2. rendering an HTML email in the browser
|
||
|
||
*** Setup
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Mu4e-Setup-1d23809e
|
||
:END:
|
||
Due to mu sitting on top of a maildir, I need to tell mu4e where said maildir
|
||
is, and point it the trash, archive, and sent folders as well as the refresh
|
||
command and how frequently I want my emails to be refreshed.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq mu4e-maildir "~/.mail"
|
||
mu4e-trash-folder "/Trash"
|
||
mu4e-refile-folder "/Archive"
|
||
mu4e-sent-folder "/Sent"
|
||
mu4e-drafts-folder "/Drafts"
|
||
mu4e-get-mail-command "mbsync -a"
|
||
mu4e-update-interval 60)
|
||
#+END_SRC
|
||
|
||
This source block is an example of the search queries in mu4e, and part of the
|
||
reason why I very much like mu4e: these bookmarks are actually defined by search
|
||
queries, but act as if they were just yet another type of custom inbox you get
|
||
with modern Email client (and often you don’t even get them). All these
|
||
bookmarks can be accessed through a shortcut on the main mu4e buffer, prefixed
|
||
by ~b~. So, for instance, my unread messages are accessed through ~bU~.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq mu4e-bookmarks
|
||
`((,(s-join " "
|
||
'("NOT flag:trashed"
|
||
"AND (maildir:/Inbox OR maildir:/Junk)"
|
||
"AND NOT to:CONLANG@LISTSERV.BROWN.EDU"
|
||
"AND NOT to:AUXLANG@LISTSERV.BROWN.EDU"
|
||
"AND NOT to:ateliers-emacs@framalistes.org"
|
||
"AND NOT to:ateliers-paris@emacs-doctor.com"
|
||
"AND NOT list:ateliers-emacs.framalistes.org"
|
||
"AND NOT list:ateliers-paris.emacs-doctor.com"))
|
||
"Inbox" ?i) ;; Inbox without the linguistics mailing lists
|
||
(,(s-join " "
|
||
'("NOT flag:trashed"
|
||
"AND (maildir:/Inbox OR maildir:/Junk)"
|
||
"AND (f:/.*up8\.edu|.*univ-paris8.*/"
|
||
"OR c:/.*up8\.edu|.*univ-paris8.*/"
|
||
"OR t:/.*up8\.edu|.*univ-paris8.*/)"))
|
||
"University" ?u) ;; University-related emails
|
||
(,(s-join " "
|
||
'("to:CONLANG@LISTSERV.BROWN.EDU"
|
||
"OR to:AUXLANG@LISTSERV.BROWN.EDU"))
|
||
"Linguistics" ?l) ;; linguistics mailing lists
|
||
(,(s-join " "
|
||
'("list:ateliers-emacs.framalistes.org"
|
||
"OR to:ateliers-paris@emacs-doctor.com"
|
||
"OR list:ateliers-paris.emacs-doctor.com"))
|
||
"Emacs" ?e) ;; Emacs mailing list
|
||
("maildir:/Sent" "Sent messages" ?s)
|
||
("flag:unread AND NOT flag:trashed" "Unread messages" ?U)
|
||
("date:today..now AND NOT flag:trashed" "Today's messages" ?t)
|
||
("date:7d..now AND NOT flag:trashed" "Last 7 days" ?w)
|
||
("date:1m..now AND NOT flag:trashed" "Last month" ?m)
|
||
("date:1y..now AND NOT flag:trashed" "Last year" ?y)
|
||
("flag:trashed AND NOT flag:trashed" "Trash" ?T)
|
||
("mime:image/* AND NOT flag:trashed" "Messages with images" ?p)))
|
||
#+END_SRC
|
||
|
||
On new email arrival, Emacs can send the system a notification which will be
|
||
handled as any other notification received by the system and will display the
|
||
number of unread emails to the user; in my case, notifications are handled by
|
||
AwesomeWM.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq mu4e-enable-notifications t
|
||
mu4e-alert-email-notification-types '(count))
|
||
(with-eval-after-load 'mu4e-alert
|
||
(mu4e-alert-set-default-style 'notifications))
|
||
(add-hook 'mu4e-view-mode-hook 'visual-line-mode)
|
||
#+END_SRC
|
||
|
||
This is the setup I have for my SMTP mail server: I point Emacs’ SMTP services
|
||
to my private mail server on its SMTP port, which should be used with a STARTTLS
|
||
stream. And I tell Emacs this is the default way to send an email.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq smtpmail-smtp-server "mail.phundrak.com"
|
||
smtpmail-smtp-service 587
|
||
smtpmail-stream-type 'starttls
|
||
message-send-mail-function 'smtpmail-send-it)
|
||
#+END_SRC
|
||
|
||
I wish my emails to be signed by default using PGP/MIME. ~mu4e~ uses ~message~
|
||
for composing new emails, so I simply need to add the function that will add the
|
||
signature to emails to the hook called when creating a new email.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(add-hook 'mu4e-compose-mode-hook 'mml-secure-message-sign-pgpmime)
|
||
#+END_SRC
|
||
|
||
mu4e used to be able to export emails to PDFs, but unfortunately this
|
||
possibility was discontinued. But we can (sort of) bring it back!
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun mu4e-action-open-as-pdf (msg)
|
||
"Export and open as PDF a mu4e `MSG'"
|
||
(let* ((date (mu4e-message-field msg :date))
|
||
(infile (mu4e~write-body-to-html msg))
|
||
(outfile (format-time-string "/tmp/%Y-%m-%d%H%M%S.pdf" date)))
|
||
(with-temp-buffer
|
||
(shell-command
|
||
(format "wkhtmltopdf %s %s" infile outfile) t))
|
||
(find-file outfile)))
|
||
|
||
(add-to-list 'mu4e-view-actions '("PDF view" . mu4e-action-open-as-pdf) t)
|
||
#+END_SRC
|
||
|
||
Lastly, some emails are better displayed in a browser than in Emacs. My Emacs
|
||
build has the webkit browser enabled, so I’ll add an option to open with it
|
||
emails.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun phundrak/mu4e-view-in-browser (msg)
|
||
(xwidget-webkit-browse-url (concat "file://"
|
||
(mu4e~write-body-to-html msg))))
|
||
|
||
(add-to-list 'mu4e-view-actions
|
||
'("Xwidget Webkit Browser" . phundrak/mu4e-view-in-browser)
|
||
t)
|
||
#+END_SRC
|
||
|
||
*** Visual Configuration
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Mu4e-Visual-Configuration-34f56f7e
|
||
:END:
|
||
The following also allows me to automatically include my signature in my Emails,
|
||
to view images in my Emacs buffers and to show me the address of my contacts and
|
||
not just their names.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq mu4e-compose-signature-auto-include t
|
||
mu4e-view-show-images t
|
||
mu4e-view-prefer-html t
|
||
mu4e-view-show-addresses t)
|
||
#+END_SRC
|
||
|
||
Now this hook is added so I can get a maximal width for the text of my emails, I
|
||
really don’t like it when lines are kilometers long. I would like instead to
|
||
hook ~visual-line-mode~ and ~auto-fill-mode~, but for some reasons Emacs throws
|
||
an error when I add them, So I go with ~visual-fill-column-mode~ instead.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(add-hook 'mu4e-view-mode-hook 'visual-fill-column-mode)
|
||
#+END_SRC
|
||
|
||
Icons are nice and all, but my current font does not display some of the default
|
||
icons set by mu4e. Due to this, I will define back these icons to the original
|
||
characters defined by mu4e:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq mu4e-headers-draft-mark '("D" . "D")
|
||
mu4e-headers-flagged-mark '("F" . "F")
|
||
mu4e-headers-new-mark '("N" . "N")
|
||
mu4e-headers-passed-mark '("P" . "P")
|
||
mu4e-headers-replied-mark '("R" . "R")
|
||
mu4e-headers-seen-mark '("S" . "S")
|
||
mu4e-headers-trashed-mark '("T" . "T")
|
||
mu4e-headers-attach-mark '("a" . "a")
|
||
mu4e-headers-encrypted-mark '("x" . "x")
|
||
mu4e-headers-signed-mark '("s" . "s")
|
||
mu4e-headers-unread-mark '("u" . "u"))
|
||
#+END_SRC
|
||
|
||
I don’t like the American time format. I really don’t. I prefer much more
|
||
something more standard, like ISO8601 standard. Not exactly ISO8601, but close
|
||
to it. Also, fuck the paywalls imposed by ISO.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq mu4e-view-date-format "%Y-%m-%d %R"
|
||
mu4e-headers-date-format "%Y-%m-%d")
|
||
#+END_SRC
|
||
|
||
*** Misc
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Mu4e-Misc-9c7a4f5b
|
||
:END:
|
||
I am unsure yet if this has any effect on mu4e, but this variable should
|
||
discourage mu4e from reading rich text emails and instead open them as plain
|
||
text. However, I do not wish to discourage opening HTML emails since I can open
|
||
them in the browser.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq mm-discouraged-alternatives '("text/richtext"))
|
||
#+END_SRC
|
||
|
||
** Miscellaneous
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Miscellaneous-d230bc2f
|
||
:END:
|
||
I have a lot of variables that need to be set but don’t fall in any other
|
||
category, so I’ll collect them here.
|
||
|
||
I have this regexp for detecting paragraphs.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq paragraph-start "\f\\|[ \t]*$\\|[ \t]*[-+*] ")
|
||
#+END_SRC
|
||
|
||
And this variable for Elcord so the main icon displayed in Discord is the icon
|
||
representing the current major-mode. I also don’t want to display the small
|
||
icon, so let’s get rid of that.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq elcord-use-major-mode-as-main-icon t
|
||
elcord-show-small-icon nil)
|
||
#+END_SRC
|
||
|
||
*** Pinentry
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Miscellaneous-Pinentry-95004d5a
|
||
:END:
|
||
Pinentry should use the ~loopback~ mode when communicating with GnuPG. Let’s set
|
||
it so:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq epg-pinentry-mode 'loopback)
|
||
#+END_SRC
|
||
|
||
*** Wttr.in
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Miscellaneous-Wttr.in_cities-dd24f8c5
|
||
:END:
|
||
Thanks to the wttrin package, I can get the weather forecast in Emacs for a
|
||
couple of cities. I just need to specify them to Emacs like so:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq wttrin-default-cities '("Aubervilliers" "Paris" "Lyon" "Nonières"
|
||
"Saint Agrève"))
|
||
#+END_SRC
|
||
|
||
However, the package is currently broken (it was last updated in 2017): wttr.in
|
||
now returns by default an HTML page instead of an ASCII result. In order to fix
|
||
it, a ~?A~ must be added at the end of the request in order to get a nice
|
||
output. Also, let’s use the HTTPS protocol while we’re at it.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun wttrin-fetch-raw-string (query)
|
||
"Get the weather information based on your QUERY."
|
||
(let ((url-user-agent "curl"))
|
||
(add-to-list 'url-request-extra-headers wttrin-default-accept-language)
|
||
(with-current-buffer
|
||
(url-retrieve-synchronously
|
||
(format "http%s://wttr.in/%s?A"
|
||
(if (gnutls-available-p) "s" "")
|
||
query)
|
||
(lambda (status)
|
||
(switch-to-buffer (current-buffer))))
|
||
(decode-coding-string (buffer-string)
|
||
'utf-8))))
|
||
#+END_SRC
|
||
|
||
** Nov-mode
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Nov-mode-6f10765d
|
||
:END:
|
||
~nov-mode~ is the mode used in the Epub reader. Here I will write a little
|
||
function that I will call through a hook each time I’m opening a new EPUB file.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun my-nov-font-setup ()
|
||
(face-remap-add-relative 'variable-pitch :family "Charis SIL"
|
||
:size 16
|
||
:height 1.0))
|
||
#+END_SRC
|
||
|
||
Let’s bind this function to the ~nov-mode~ hook. By the way, we’ll also enable
|
||
the ~visual-line-mode~ here, just in case.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(mapc (lambda (mode)
|
||
(add-hook 'nov-mode-hook mode))
|
||
'('my-nov-font-setup 'visual-line-mode))
|
||
#+END_SRC
|
||
|
||
Let’s also set the maximum length of the lines in ~nov-mode~:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq nov-text-width 80)
|
||
#+END_SRC
|
||
|
||
** Programming
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Programming-b4b565ae
|
||
:END:
|
||
*** LSP
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-LSP-4f8aa691
|
||
:END:
|
||
When it comes to the LSP layer, there are some options which are not enabled by
|
||
default that I want to use, especially some modes I want to take advantage of.
|
||
This is why I enable first the ~lsp-treemacs-sync-mode~ so treemacs is LSP
|
||
aware:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(lsp-treemacs-sync-mode 1)
|
||
#+END_SRC
|
||
|
||
I also enable some layers related to ~dap~, the Debug Adapter Protocol, which
|
||
works really nicely with LSP. Let’s enable Dap’s modes:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(dap-mode 1)
|
||
(dap-ui-mode 1)
|
||
(dap-tooltip-mode 1)
|
||
#+END_SRC
|
||
|
||
Finally, I also want the documentation tooltip to show up when the cursor is
|
||
above a documented piece of code or symbol. Let’s enable that too:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(tooltip-mode 1)
|
||
#+END_SRC
|
||
|
||
*** ASM configuration
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-ASM_configuration-f6dc7674
|
||
:END:
|
||
The first thing I will set with my ASM configuration is where the reference PDF
|
||
is located.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq x86-lookup-pdf "~/Documents/code/asm/Intelx86/325383-sdm-vol-2abcd.pdf")
|
||
#+END_SRC
|
||
|
||
I will also modify what the comment character is, from a ~;~ to a ~#~:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq asm-comment-char ?\#)
|
||
#+END_SRC
|
||
|
||
*** C/C++
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-C-C++-76c3f997
|
||
:END:
|
||
As the C/C++ syntax is checked by flycheck, let’s make sure we are using the
|
||
latest standard available, that is C++17 and C17, from Clang.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(add-hook 'c-mode-hook
|
||
(lambda ()
|
||
(setq flycheck-clang-language-standard "c17")))
|
||
(add-hook 'c++-mode-hook
|
||
(lambda ()
|
||
(setq flycheck-clang-language-standard "c++17")))
|
||
#+END_SRC
|
||
|
||
*** Dart configuration
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Dart_configuration-ecf24ebf
|
||
:END:
|
||
For Dart, I mainly declared some custom shortcuts bound to ~dart-mode~ related
|
||
to flutter, so nothing too exciting here. Some prefix are declared in order to
|
||
avoid the shortcuts in helm to show up as just ~custom~.
|
||
#+begin_src emacs-lisp
|
||
(spacemacs/declare-prefix-for-mode 'dart-mode "mo" "user-defined")
|
||
(spacemacs/declare-prefix-for-mode 'dart-mode "mof" "flutter")
|
||
(spacemacs/declare-prefix-for-mode 'dart-mode "mofr" "flutter-run")
|
||
#+end_src
|
||
|
||
Now, for the shortcuts themselves:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(spacemacs/set-leader-keys-for-major-mode 'dart-mode
|
||
"ofH" 'flutter-hot-restart
|
||
"ofh" 'flutter-hot-reload
|
||
"ofq" 'flutter-quit
|
||
"ofr" (lambda () (interactive) (flutter-run "-v"))
|
||
"ofs" 'flutter-screenshot)
|
||
#+END_SRC
|
||
|
||
*** Emacs Lisp
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Emacs_Lisp-59230f3c
|
||
:END:
|
||
Here will be stored my configuration directly related to Emacs Lisp, including
|
||
some functions or default modes.
|
||
|
||
**** Enable ~eldoc-mode~ by default
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Emacs_Lisp-Enable_~eldoc-mode~_by_default-f131abde
|
||
:END:
|
||
By default, if some Elisp code is opened, I want to enable ~eldoc-mode~ so I can
|
||
easily get some documentation on the symbols in the source code. This is done
|
||
via the use of hooks.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(add-hook 'prog-mode-hook 'eldoc-mode)
|
||
#+END_SRC
|
||
|
||
**** ~phundrak/write-to-buffer~
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Emacs_Lisp-~phundrak-write-to-buffer~-2f192dd3
|
||
:END:
|
||
I was very surprised when I discovered no such function exists in Elisp. This
|
||
function basically writes a string into a buffer, and optionally switches the
|
||
user to the buffer. Here is the code for that function:
|
||
#+BEGIN_SRC elisp :results silent
|
||
(defun write-to-buffer ($input-string $outputbuf &optional $switchbuf)
|
||
"Writes `$input-string' to the specified `output-buffer'. If
|
||
`switch-buffer' is non-nil, the active buffer will switch to the
|
||
output buffer; otherwise, it will take the user back to their
|
||
initial buffer. Works with `$input-string' as a string or a list
|
||
of strings."
|
||
(let ((oldbuf (current-buffer)))
|
||
(switch-to-buffer $outputbuf)
|
||
(cond ((char-or-string-p $input-string) (insert $input-string))
|
||
((listp $input-string) (dolist (elem $input-string)
|
||
(insert (format "%s\n" elem)))))
|
||
(unless $switchbuf
|
||
(switch-to-buffer oldbuf))))
|
||
#+END_SRC
|
||
|
||
*** Python
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Python-9cdd1b06
|
||
:END:
|
||
Emacs throws me an error about the python interpreter, let’s silence it:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq python-shell-completion-native-disabled-interpreters '("python"))
|
||
#+END_SRC
|
||
|
||
*** Rust
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Rust-ba633575
|
||
:END:
|
||
I need to point to racer where the source code of Rust is located so I can get
|
||
some documentation. This is installed with the ~rust-src~ component you can get
|
||
through ~rustup~. To install it, simply run
|
||
#+BEGIN_SRC sh :tangle no :exports code
|
||
rustup component add rust-src
|
||
#+END_SRC
|
||
|
||
Now, the source code for Rust should be included in your installation. I
|
||
personally prefer to develop with Rust stable, so let’s indicate to Emacs to
|
||
search for documentation in the stable sources:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq racer-rust-src-path
|
||
"~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src")
|
||
#+END_SRC
|
||
|
||
Rust’s default ~cargo check~ command is already very good, however I also enjoy
|
||
getting some more hints while developping, and ~clippy~ does a very good job at
|
||
it. To get clippy, I need to run the following to install it:
|
||
#+BEGIN_SRC sh :tangle no :exports code
|
||
rustup compontent add clippy
|
||
#+END_SRC
|
||
|
||
And this will get it installed with all of my Rust toolchain, and it will be
|
||
updated with it. Now, let’s indicate LSP that I want to use that instead of
|
||
~check~:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq lsp-rust-analyzer-cargo-watch-command "clippy")
|
||
#+END_SRC
|
||
|
||
Finally, I wish to enable ~electric-pair-mode~ and ~indent-guide-mode~ for Rust
|
||
files, so let’s enable that through the use of a hook:
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
(add-hook 'rust-mode-hook
|
||
'(lambda ()
|
||
(local-set-key (kbd "TAB") #'company-indent-or-complete-common)
|
||
(electric-pair-mode 1)))
|
||
#+END_SRC
|
||
|
||
*** Scheme
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Scheme-e35aa50a
|
||
:END:
|
||
The Scheme configuration will be very short, I just need to tell Emacs the name
|
||
of the interpreter since it is not the default one:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq geiser-chicken-binary "chicken-csi")
|
||
#+END_SRC
|
||
|
||
** Projectile
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Projectile-06e580f0
|
||
:END:
|
||
Projectile is an awesome utility which helps managing projects within Emacs. It
|
||
will automatically detect version controlled directories, and will by default
|
||
assume this is a project I can be working on. However, there are some
|
||
directories that are version controlled that I do not want to see in my list of
|
||
projects, namely all the cached AUR packages from my AUR helper, ~paru~. They
|
||
are all stored in the same parent directory, so let’s ignore that. I will also
|
||
make Emacs ignore all ~node_modules~ directories it could encounter. And for
|
||
some reason, =~/.config/emacs= is always in my projects list (I now use
|
||
XDG-compliant directories), so let’s also ignore that.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq projectile-ignored-projects '("~/.cache/paru" "~/.config/emacs" "/tmp"))
|
||
(add-to-list 'projectile-globally-ignored-directories "node_modules")
|
||
#+END_SRC
|
||
|
||
** Security
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Security-21d88555
|
||
:END:
|
||
This paragraph is about making Emacs and GPG as a whole (since Emacs is /always/
|
||
open on my computer) more secure. The first thing I want to make is a function
|
||
that will close any buffer that contains an open ~.gpg~ file –I certainly do not
|
||
want anyone to be able to read such files on my computer if I leave it even for
|
||
a couple of minutes.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defun phundrak/kill-gpg-buffers ()
|
||
"Kill GPG buffers."
|
||
(interactive)
|
||
(let ((buffers-killed 0))
|
||
(dolist (buffer (buffer-list))
|
||
(with-current-buffer buffer
|
||
(when (string-match ".*\.gpg$" (buffer-name buffer))
|
||
(message "Auto killing .gpg buffer '%s'" (buffer-name buffer))
|
||
(when (buffer-modified-p buffer)
|
||
(save-buffer))
|
||
(kill-buffer buffer)
|
||
(setq buffers-killed (+ buffers-killed 1)))))
|
||
(unless (zerop buffers-killed)
|
||
;; Kill gpg-agent.
|
||
(shell-command "gpgconf --kill gpg-agent")
|
||
(message "%s .gpg buffers have been autosaved and killed" buffers-killed))))
|
||
#+END_SRC
|
||
|
||
Notice the ~(shell-command "gpgconf --kill gpg-agent")~ command there: it kills
|
||
~gpg-agent~ which will always respawn each time GPG2 is invoked. That way, I
|
||
know anyone trying to open a GPG file will have to insert my password when
|
||
trying to do so instead of just hoping I entered it not long ago and they won’t
|
||
have to.
|
||
|
||
But surely, if I only define this function and hope to call it each time I leav
|
||
my computer, surely at one point I will forget to execute it before leaving. I
|
||
can’t trust myself to always call it manually. Which is why I’ll ask Emacs
|
||
itself to call it after it detects a minute of idling. It may become from times
|
||
to times a bit of a pain, but at least I’m now sure I won’t ever have to worry
|
||
about someone reading my GPG files open in Emacs while I’m out for a quick
|
||
break.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(run-with-idle-timer 60 t 'phundrak/kill-gpg-buffers)
|
||
#+END_SRC
|
||
|
||
** Snippets
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Snippets-67a32065
|
||
:HEADER-ARGS:snippet: :padline no :mkdirp yes
|
||
:END:
|
||
Yasnippet’s snippets tool is extremely powerful and allows me to write very
|
||
quickly code. For now, we have snippets for two modes. The files you’ll see
|
||
below are exported to ~$HOME/.config/emacs/private/snippets/~ and to their
|
||
respective mode directory. For instance, my ~caption~ snippet for org-mode will
|
||
be exported to ~$HOME/.config/emacs/private/snippets/org-mode/caption~.
|
||
|
||
Be aware that on top of these custom snippets, I also use the package
|
||
[[file:awesome.org::#Autostart-f2cf42fe][yasnippet-snippets]] which provide plenty of already made snippets.
|
||
|
||
*** Rust snippets
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Snippets-Rust_snippets-b106fad4
|
||
:END:
|
||
I have so far two snippets, the first one is actually just a convenience to make
|
||
it easier to type a ~println!~ macro than the default snippet.
|
||
#+BEGIN_SRC snippet :tangle ~/.config/emacs/private/snippets/rust-mode/println
|
||
# -*- mode: snippet -*-
|
||
# name: println
|
||
# key: pln
|
||
# --
|
||
println!("${1:{}}", $2);
|
||
#+END_SRC
|
||
|
||
The second one is more interesting: it is used to create a ~new~ method for a
|
||
struct, and it will try to create a function that will assign each argument
|
||
passed to the method to members of the struct. It relies on the custom function
|
||
[[#Custom-functions-yas-rust-new-assignments-4ad16bde][I wrote here]].
|
||
#+BEGIN_SRC snippet :tangle ~/.config/emacs/private/snippets/rust-mode/new
|
||
# -*- mode: snippet -*-
|
||
# name: new
|
||
# key: _new
|
||
# --
|
||
fn new(${1:args}) -> Self {
|
||
$0
|
||
Self {
|
||
${1:$(phundrak-yas-rust-new-assignments yas-text)}
|
||
}
|
||
}
|
||
#+END_SRC
|
||
|
||
*** Org headers
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Snippets-org-mode_snippets-Org_headers-ed14fbac
|
||
:END:
|
||
The first two snippets are used to add HTML or LaTeX attributes to elements in
|
||
org-mode. The third also has a similar usage, inserting a ~#+CAPTION~ header
|
||
before an element, as well as the fourth which inserts a ~#+NAME~ header.
|
||
#+BEGIN_SRC snippet :tangle ~/.config/emacs/private/snippets/org-mode/attr_html
|
||
# -*- mode: snippet -*-
|
||
# name: ATTR HTML
|
||
# key: <ah
|
||
# --
|
||
,#+ATTR_HTML: $0
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC snippet :tangle ~/.config/emacs/private/snippets/org-mode/attr_latex
|
||
# -*- mode: snippet -*-
|
||
# name: ATTR LATEX
|
||
# key: <al
|
||
# --
|
||
,#+ATTR_LATEX: $0
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC snippet :tangle ~/.config/emacs/private/snippets/org-mode/caption
|
||
# -*- mode: snippet -*-
|
||
# name: caption
|
||
# key: <ca
|
||
# --
|
||
,#+CAPTION: $0
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC snippet :tangle ~/.config/emacs/private/snippets/org-mode/name
|
||
# -*- mode: snippet -*-
|
||
# name: name
|
||
# key: <na
|
||
# --
|
||
,#+NAME: $0
|
||
#+END_SRC
|
||
|
||
Now, the following is a bit more complex: it is meant to be used as a new org
|
||
buffer is created. It will insert an org header for the title, which will
|
||
default to the buffer’s name capitalized minus the dashes or underscores
|
||
replaced with spaces, it will insert a default author and email based on the
|
||
user’s parameters, and the date at the moment of the creation of these headers.
|
||
The user can also add some tags if they wish to.
|
||
#+BEGIN_SRC snippet :tangle ~/.config/emacs/private/snippets/org-mode/header
|
||
# -*- mode: snippet -*-
|
||
# name: header
|
||
# key: header
|
||
# --
|
||
,#+TITLE: ${1:`(replace-regexp-in-string "-" " " (capitalize (file-name-nondirectory (file-name-sans-extension (buffer-file-name)))))`}
|
||
,#+AUTHOR: `(user-full-name)`
|
||
,#+EMAIL: `user-mail-address`
|
||
,#+DATE: `(format-time-string "%Y-%m-%d")`
|
||
,#+TAGS: $2
|
||
|
||
$0
|
||
#+END_SRC
|
||
|
||
*** Org blocks
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Snippets-org-mode_snippets-Org_blocks-e4dfc448
|
||
:HEADER-ARGS:snippet: :padline no :mkdirp yes
|
||
:END:
|
||
Now, Let’s write some snippets for org blocks. The first one is for a comment
|
||
block, then two snippets for an unnamed and a named Elisp source block, and two
|
||
others for an unnamed and a named Python source block. There are also two block
|
||
for generic unnamed source blocks and generic named source blocks.
|
||
#+BEGIN_SRC snippet :tangle ~/.config/emacs/private/snippets/org-mode/comment_block
|
||
# -*- mode: snippet -*-
|
||
# name: comment block
|
||
# key: <co
|
||
# --
|
||
,#+BEGIN_COMMENT
|
||
$0
|
||
,#+END_COMMENT
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC snippet :tangle ~/.config/emacs/private/snippets/org-mode/emacs-lisp
|
||
# -*- mode: snippet -*-
|
||
# name: emacs-lisp block
|
||
# key: <el
|
||
# --
|
||
,#+BEGIN_SRC emacs-lisp
|
||
$0
|
||
,#+END_SRC
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC snippet :tangle ~/.config/emacs/private/snippets/org-mode/emacs-lisp-named
|
||
# -*- mode: snippet -*-
|
||
# name: named emacs-lisp block
|
||
# key: <eln
|
||
# --
|
||
,#+NAME: $1
|
||
,#+BEGIN_SRC emacs-lisp
|
||
$0
|
||
,#+END_SRC
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC snippet :tangle ~/.config/emacs/private/snippets/org-mode/python
|
||
# -*- mode: snippet -*-
|
||
# name: python block
|
||
# key: <py
|
||
# --
|
||
,#+BEGIN_SRC python
|
||
$0
|
||
,#+END_SRC
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC snippet :tangle ~/.config/emacs/private/snippets/org-mode/python-named
|
||
# -*- mode: snippet -*-
|
||
# name: named python block
|
||
# key: <pyn
|
||
# --
|
||
,#+NAME: $1
|
||
,#+BEGIN_SRC python
|
||
$0
|
||
,#+END_SRC
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC snippet :tangle ~/.config/emacs/private/snippets/org-mode/src
|
||
# -*- mode: snippet -*-
|
||
# name: source block
|
||
# key: <s
|
||
# --
|
||
,#+BEGIN_SRC $1
|
||
$0
|
||
,#+END_SRC
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC snippet :tangle ~/.config/emacs/private/snippets/org-mode/src-named
|
||
# -*- mode: snippet -*-
|
||
# name: named source block
|
||
# key: <sn
|
||
# --
|
||
,#+NAME: $1
|
||
,#+BEGIN_SRC $2
|
||
$0
|
||
,#+END_SRC
|
||
#+END_SRC
|
||
|
||
*** Org misc
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Snippets-Org_misc-82223a44
|
||
:END:
|
||
Finally, there are a couple of miscellaneous org snippets that insert macros I
|
||
often use in my conlanging documents. The first one inserts a phonetics macro,
|
||
while the second one inserts a macro used for my Proto-Ñyqy language.
|
||
#+BEGIN_SRC snippet :tangle ~/.config/emacs/private/snippets/org-mode/phon
|
||
# -*- mode: snippet -*-
|
||
# name: phon
|
||
# key: <ph
|
||
# --
|
||
\{\{\{phon($1)\}\}\} $0
|
||
#+END_SRC
|
||
|
||
#+BEGIN_SRC snippet :tangle ~/.config/emacs/private/snippets/org-mode/phon
|
||
# -*- mode: snippet -*-
|
||
# name: nyqy
|
||
# key: <ny
|
||
# --
|
||
\{\{\{nyqy($1)\}\}\} $0
|
||
#+END_SRC
|
||
|
||
** Tramp configuration
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Tramp_configuration-acb4733b
|
||
:END:
|
||
*** Docker
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Tramp_configuration-Docker-31573fdf
|
||
:END:
|
||
It is completely possible with Tramp to connect ot a docker container and modify
|
||
files inside of it. It is not supported natively, but we can add it quite
|
||
easily. Be aware, I am not the author of this code, you can find its original
|
||
source [[https://www.emacswiki.org/emacs/TrampAndDocker][here]]. First, let’s add the Docker protocol to Tramp:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(push
|
||
(cons
|
||
"docker"
|
||
'((tramp-login-program "docker")
|
||
(tramp-login-args (("exec" "-it") ("%h") ("/bin/bash")))
|
||
(tramp-remote-shell "/bin/sh")
|
||
(tramp-remote-shell-args ("-i") ("-c"))))
|
||
tramp-methods)
|
||
#+END_SRC
|
||
|
||
Now that the method has been added, let’s add some autocompletion for when we
|
||
want to connect to a Docker container:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(defadvice tramp-completion-handle-file-name-all-completions
|
||
(around dotemacs-completion-docker activate)
|
||
"(tramp-completion-handle-file-name-all-completions \"\" \"/docker:\" returns
|
||
a list of active Docker container names, followed by colons."
|
||
(if (equal (ad-get-arg 1) "/docker:")
|
||
(let* ((dockernames-raw (shell-command-to-string "docker ps --format '{{.Names}}:'"))
|
||
(dockernames (cl-remove-if-not #'(lambda (dockerline)
|
||
(string-match ":$" dockerline))
|
||
(split-string dockernames-raw "\n"))))
|
||
(setq ad-return-value dockernames))
|
||
ad-do-it))
|
||
#+END_SRC
|
||
|
||
And that’s it! it is now possible to connect to a docker container with
|
||
something like ~/docker:conlangdict_server/~ as the path given in ~find-file~.
|
||
|
||
*** Yadm
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Yadm-4344fec3
|
||
:END:
|
||
~yadm~ is the utility I use for managing my dotfiles, and it is a wrapper In
|
||
order to manage my dotfiles, I use the following shortcut to launch Magit Status
|
||
for ~yadm~:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(spacemacs/declare-prefix "oy" "yadm status")
|
||
(spacemacs/set-leader-keys "oy" (lambda () (interactive) (magit-status "/yadm::")))
|
||
#+END_SRC
|
||
~yadm~ wraps around ~git~. Logically, it means Magit could theoretically manage
|
||
my yadm repo. And it is indeed possible, according to [[https://github.com/TheLocehiliosan/yadm/blob/master/yadm.md][this page]] using TRAMP. I
|
||
just need to add the following code snippet:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(add-to-list 'tramp-methods
|
||
'("yadm"
|
||
(tramp-login-program "yadm")
|
||
(tramp-login-args (("enter")))
|
||
(tramp-login-env (("SHELL")
|
||
("/bin/sh")))
|
||
(tramp-remote-shell "/bin/sh")
|
||
(tramp-remote-shell-args ("-c"))))
|
||
#+END_SRC
|
||
|
||
** Visual configuration
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Visual_configuration-78e6cafc
|
||
:END:
|
||
*** Battery mode line
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Editing_and_modes-Battery_mode_line-895e5e52
|
||
:END:
|
||
I want to see by default how much battery my computer has, so let’s enable it:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(spacemacs/toggle-mode-line-battery-on)
|
||
#+END_SRC
|
||
|
||
*** Better faces
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Visual-configuration-Better-faces-5d73fe9c
|
||
:END:
|
||
#+NAME: face-generate
|
||
#+BEGIN_SRC emacs-lisp :tangle no :results replace :exports none :var input=mu4e-faces :var makeface="yes"
|
||
(let* ((makeface (string= "yes" makeface))
|
||
(headers (cadr input))
|
||
(faces (-map (lambda (face)
|
||
(phundrak-filter (lambda (elem)
|
||
(or (numberp (cadr elem))
|
||
(not (string= ""
|
||
(cadr elem)))))
|
||
(phundrak-zip headers face)))
|
||
(cddr input))))
|
||
(mapconcat (lambda (face)
|
||
(concat
|
||
;; (if makeface
|
||
;; (format "`(%s ((t " (cadr (assoc "Name" face)))
|
||
;; (format "(%s `" (cadr (assoc "Name" face))))
|
||
(format (if makeface "`(%s ((t " "(%s `")
|
||
(cadr (assoc "Name" face)))
|
||
(format "(%s %s %s %s)"
|
||
(if (assoc "additional" face)
|
||
(cadr (assoc "additional" face))
|
||
"")
|
||
(let ((result ""))
|
||
(dolist (property '("inherit" "weight" "height" "foreground"
|
||
"background" "underline")
|
||
result)
|
||
(let ((prop (assoc property face)))
|
||
(when prop
|
||
(setf result (format "%s :%s %s"
|
||
result
|
||
property
|
||
(if (numberp (cadr prop))
|
||
(number-to-string (cadr prop))
|
||
(cadr prop))))))))
|
||
(if (assoc "font" face)
|
||
(format ":font \"%s\""
|
||
(cadr (assoc "font" face)))
|
||
"")
|
||
(if (assoc "family" face)
|
||
(format ":family \"%s\""
|
||
(cadr (assoc "family" face)))
|
||
""))
|
||
(if makeface
|
||
")) t)"
|
||
")")))
|
||
faces
|
||
"\n"))
|
||
#+END_SRC
|
||
|
||
Sometimes, some visual properties just don’t fit right for me and I need to edit
|
||
them. This is the case for example for org-mode for which I want to have a mix
|
||
of fixed and variable pitches. Below you can see the code that does that for me,
|
||
I’ll get into more detail below this code block.
|
||
#+BEGIN_SRC emacs-lisp :noweb yes
|
||
(let (
|
||
<<face-generate(input=org-common-faces, makeface="no")>>
|
||
)
|
||
(custom-theme-set-faces
|
||
'user
|
||
<<face-generate(input=diff-faces)>>
|
||
<<face-generate(input=mu4e-faces)>>
|
||
<<face-generate(input=org-faces)>>
|
||
))
|
||
#+END_SRC
|
||
|
||
**** Diff and Magit
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Visual-configuration-Better-faces-Diff-6ac98986
|
||
:END:
|
||
Apparently, diff and Magit faces do not follow the nord theme’s color scheme, so
|
||
let’s redefine their background and sometimes their foreground.
|
||
#+tblname: diff-faces
|
||
| / | |
|
||
| Name | background |
|
||
|----------------------+------------------|
|
||
| ediff-current-diff-A | ,phundrak-nord11 |
|
||
| ediff-current-diff-C | ,phundrak-nord13 |
|
||
| ediff-current-diff-C | ,phundrak-nord14 |
|
||
|
||
**** Mu4e
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Visual-configuration-Better-faces-Mu4e-7b52d940
|
||
:END:
|
||
The nord theme is great and all, but for some reason some faces in mu4e aren’t
|
||
displayed properly, such as the ~mu4e-highlight-face~. Let’s fix that!
|
||
#+tblname: mu4e-faces
|
||
| / | | |
|
||
| Name | background | foreground |
|
||
|---------------------+-----------------+-----------------|
|
||
| mu4e-highlight-face | ,phundrak-nord9 | ,phundrak-nord0 |
|
||
|
||
**** Org-mode
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Visual-configuration-Better-faces-Org-mode-07754177
|
||
:END:
|
||
Fonts will play an important part in this, but so will colors and font size. The
|
||
following code is largely based on the one found [[https://zzamboni.org/post/beautifying-org-mode-in-emacs/][on this blog post]] and [[https://lepisma.xyz/2017/10/28/ricing-org-mode/][this one]].
|
||
First here are some common properties that will be reused in faces below:
|
||
#+tblname: org-common-faces
|
||
| / | | < | <l> | |
|
||
| Name | inherit | font | height | weight |
|
||
|---------+---------+---------------+--------+--------|
|
||
| orgfont | | Charis SIL | 120 | |
|
||
| head | default | | | bold |
|
||
| fixed | | Cascadia Code | 0.8 | |
|
||
|
||
#+tblname: org-faces
|
||
| / | < | | | | <l> | | | |
|
||
| Name | additional | inherit | foreground | background | height | weight | italic | underline |
|
||
|---------------------------+------------------+-------------------------------+------------------+-----------------+--------+--------+--------+-----------|
|
||
| org-level-1 | ,@orgfont ,@head | | ,phundrak-nord15 | | 1.75 | | t | |
|
||
| org-level-2 | ,@orgfont ,@head | | ,phundrak-nord10 | | 1.5 | | t | |
|
||
| org-level-3 | ,@orgfont ,@head | | ,phundrak-nord9 | | 1.25 | | t | |
|
||
| org-level-4 | ,@orgfont ,@head | | ,phundrak-nord15 | | 1.1 | | t | |
|
||
| org-level-5 | ,@orgfont ,@head | | ,phundrak-nord8 | | | | t | |
|
||
| org-level-6 | ,@orgfont ,@head | | ,phundrak-nord7 | | | | t | |
|
||
| org-level-7 | ,@orgfont ,@head | | ,phundrak-nord15 | | | | t | |
|
||
| org-level-8 | ,@orgfont ,@head | | ,phundrak-nord6 | | | | t | |
|
||
| org-document-title | ,@orgfont ,@head | | ,phundrak-nord11 | | 2.0 | | t | |
|
||
| variable-pitch | ,@orgfont | | | | | | | |
|
||
| org-block | ,@fixed | | | ,phundrak-nord1 | | | | |
|
||
| org-block-begin-line | ,@fixed | | | ,phundrak-nord1 | | | | |
|
||
| org-block-end-line | ,@fixed | | | ,phundrak-nord1 | | | | |
|
||
| org-indent | ,@fixed | | | | | | | |
|
||
| org-formula | ,@fixed | | | | | | | |
|
||
| org-macro | ,@fixed | | | | | | | |
|
||
| org-target | ,@fixed | | | | | | | |
|
||
| org-property-value | ,@fixed | | | | | | | |
|
||
| org-drawer | ,@fixed | | ,phundrak-nord10 | | | | | |
|
||
| org-table | ,@fixed | | ,phundrak-nord14 | | | | | |
|
||
| org-date | ,@fixed | | ,phundrak-nord13 | | | | | |
|
||
| org-code | ,@fixed | shadow | | | | | | |
|
||
| org-verbatim | ,@fixed | shadow | | | | | | |
|
||
| org-document-info-keyword | ,@fixed | shadow | | | | | | |
|
||
| org-tag | ,@fixed | shadow | | | | bold | | |
|
||
| org-meta-line | ,@fixed | font-lock-comment-face | | | 0.8 | | | |
|
||
| org-special-keyword | ,@fixed | font-lock-comment-face | ,phundrak-nord15 | | 0.8 | | | |
|
||
| org-checkbox | ,@fixed | (org-todo shadow fixed-pitch) | | | | | | |
|
||
| org-document-info | | | ,phundrak-nord12 | | | | | |
|
||
| org-link | | | ,phundrak-nord8 | | | | | t |
|
||
|
||
*** Info colors
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Visual-configuration-Info-colors-ebe3cc81
|
||
:END:
|
||
The package ~info-colors~ adds colors to Emacs’ info mode. Let’s enable it:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(add-hook 'Info-selection-hook 'info-colors-fontify-node)
|
||
#+END_SRC
|
||
|
||
*** Prettified symbols
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User_Configuration-Miscellaneous-Prettified_symbols-da50f4a6
|
||
:END:
|
||
Just because it is pleasing to the eye, some symbols in source code get
|
||
prettified into simpler symbols. Here is the list of symbols that are to be
|
||
prettified. You can see in the corresponding comment what symbol will be
|
||
displayed.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq prettify-symbols-alist '(("lambda" . 955) ; λ
|
||
("mapc" . 8614) ; ↦
|
||
("map" . 8614) ; ↦
|
||
(">>" . 187) ; »
|
||
("<<" . 171) ; «
|
||
))
|
||
#+END_SRC
|
||
|
||
Let’s enable this mode globally.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(global-prettify-symbols-mode 1)
|
||
#+END_SRC
|
||
|
||
*** Misc
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: User-Configuration-Visual-configuration-Misc-2f4ce585
|
||
:END:
|
||
Emacs is already silent, but let’s set the bell as visible:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq visible-bell t)
|
||
#+END_SRC
|
||
|
||
I would also like to disable the global ~hl-mode~, I find it quite annoying.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(global-hl-line-mode -1)
|
||
#+END_SRC
|
||
|
||
* Footnotes
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Footnotes-5ffd05ee
|
||
:END:
|
||
|
||
[fn:2] [[https://labs.phundrak.com/phundrak/dotfiles/src/branch/master/.spacemacs][labs.phundrak.com/phundrak/dotfiles/src/branch/master/.spacemacs]]
|
||
|
||
[fn:1] [[https://labs.phundrak.com/phundrak/dotfiles/src/branch/master/org/config/emacs.org][labs.phundrak.com/phundrak/dotfiles/src/branch/master/org/config/emacs.org]]
|