Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/lccambiaghi/vanilla-emacs


https://github.com/lccambiaghi/vanilla-emacs

Last synced: 6 days ago
JSON representation

Awesome Lists containing this project

README

        

Luca's literate Emacs config

var clicky_site_ids = clicky_site_ids || []; clicky_site_ids.push(101260027);

<!--/*--><![CDATA[/*><!--*/
*,
*::before,
*::after {
box-sizing: border-box;
}

html {
line-height: 1.35;
-webkit-text-size-adjust: 100%;
}

body {
margin: 0;
font-family:
system-ui,
-apple-system,
'Segoe UI',
Roboto,
Helvetica,
Arial,
sans-serif,
'Apple Color Emoji',
'Segoe UI Emoji';
}

hr {
height: 0;
color: inherit;
}

b,
strong {
font-weight: bolder;
}

code,
kbd,
samp,
pre {
font-family:
ui-monospace,
SFMono-Regular,
Consolas,
'Liberation Mono',
Menlo,
monospace;
font-size: 1em;
}

small {
font-size: 80%;
}

sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}

sub {
bottom: -0.25em;
}

sup {
top: -0.5em;
}

table {
text-indent: 0;
border-color: inherit;
}

summary {
display: list-item;
}

:root {
--bc-tb: #e8dfd1;
--bc-block: initial;
--clr: #000000;
--clr-tb-hd: #282828;
--clr-code: #282828;
--bg: #fefcf4;
--bg-code: #e8dfd1;
--bg-tb-hd: #e8dfd1;
--bg-block: #faf6ef;
--clr-link: #0000c0;
--clr-title: #000000;
--clr-todo: #2544bb;
--bg-todo: initial;
--clr-done: #505050;
--bg-done: initial;
--ts-clr: #5317ac;
--mg: 20px;
--w-toc: 300px;
--bg-keyword: initial;
--clr-keyword: #5317ac;
--bg-constant: initial;
--clr-constant: #0000c0;
--bg-comment: initial;
--clr-comment: #505050;
--bg-comment-delimiter: initial;
--clr-comment-delimiter: initial;
--bg-function: initial;
--clr-function: #721045;
--bg-variable: initial;
--clr-variable: #00538b;
--bg-preprocessor: initial;
--clr-preprocessor: #a0132f;
--bg-doc: initial;
--clr-doc: #2a486a;
--bg-builtin: initial;
--clr-builtin: #8f0075;
--bg-string: initial;
--clr-string: #2544bb;
}

html {
font-family: Sans;
}

body {
background: #999;
display: flex;
flex-direction: column;
align-items: center;
}

body[data-theme='light'] {
background: var(--bg);
color: var(--clr);
}

body[data-theme='dark'] {
background: var(--bg);
color: var(--clr);
--bc-tb: #e8dfd1;
--bc-block: initial;
--clr: #000000;
--clr-code: #282828;
--clr-tb-hd: #282828;
--bg: #fefcf4;
--bg-tb-hd: #e8dfd1;
--bg-code: #e8dfd1;
--bg-block: #faf6ef;
--clr-link: #0000c0;
--clr-title: #000000;
--clr-todo: #2544bb;
--bg-todo: initial;
--clr-done: #505050;
--bg-done: initial;
--ts-clr: #5317ac;
--mg: 20px;
--w-toc: 300px;
--bg-keyword: initial;
--clr-keyword: #5317ac;
--bg-constant: initial;
--clr-constant: #0000c0;
--bg-comment: initial;
--clr-comment: #505050;
--bg-comment-delimiter: initial;
--clr-comment-delimiter: initial;
--bg-function: initial;
--clr-function: #721045;
--bg-variable: initial;
--clr-variable: #00538b;
--bg-preprocessor: initial;
--clr-preprocessor: #a0132f;
--bg-doc: initial;
--clr-doc: #2a486a;
--bg-builtin: initial;
--clr-builtin: #8f0075;
--bg-string: initial;
--clr-string: #2544bb;
}

@media only screen and (max-width: 768px) {
#content {
width: 100%;
padding-left: 10px;
padding-right: 10px;
}
}

@media only screen and (min-width: 768px) {
#content {
width: 768px;
}
}

@media only screen and (min-width: 1024px) {
body {
align-items: flex-end;
}
#content {
width: calc(100vw - var(--w-toc) - var(--mg) * 4);
margin-right: var(--mg);
}
}

h1 {
color: var(--clr-title);
text-align: center;
}
h2, h3, h4, h5, h6, h7 {
font-family: Sans Serif;
}
h2 {
line-height: 2.5em;
}
h2::before {
content: "◉ ";
}
h3::before {
content: "✸ ";
}
h4::before {
content: "▷ ";
}

code {
color: var(--clr-code);
background: var(--bg-code);
border-radius: 2px;
padding-left: 5px;
padding-right: 5px;
}

pre {
padding: 20px;
border: 1px solid var(--bc-block);
background: var(--bg-block);
font-size: 0.9rem;
overflow-x: scroll;
}

pre::-webkit-scrollbar {
display: none;
}

pre {
-ms-overflow-style: none;
scrollbar-width: none;
}

.todo {
color: var(--clr-todo);
background: var(--bg-todo);
}

.done {
color: var(--clr-done);
background: var(--bg-done);
}

.timestamp-wrapper {
font-size: 0.8rem;
color: var(--clr-ts);
}

table {
width: 100%;
border: 2px solid var(--bc-tb);
}

thead {
color: var(--clr-tb-hd);
background: var(--bg-tb-hd);
}

td, th {
border: thin solid var(--bc-tb);
}

li {
line-height: 2rem;
}

a {
text-decoration: none;
color: var(--clr-link);
}

a:hover {
text-decoration: underline;
}

#table-of-contents > h2 {
display: none;
}

#table-of-contents {
border: thin solid var(--bc-block);
background: var(--bg-block);
}

#toggle-toc {
display: none;
}

.toc-show {
display: block!important;
}

@media only screen and (max-width: 768px) {
#toggle-toc {
position: fixed;
top: 0;
right: 0;
cursor: pointer;
padding-left: 10px;
padding-right: 10px;
font-size: 2em;
line-height: 2em;
display: block;
}
#table-of-contents {
background: var(--bg);
display: none;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
overflow-y: scroll;
}
#table-of-contents a {
color: var(--clr);
}
}

@media only screen and (min-width: 1024px) {
#table-of-contents {
font-size: 0.8rem;
position: fixed;
left: var(--mg);
top: var(--mg);
bottom: var(--mg);
width: var(--w-toc);
overflow-y: scroll;
}
}

#table-of-contents::-webkit-scrollbar {
display: none;
}

#table-of-contents {
-ms-overflow-style: none;
scrollbar-width: none;
}

.MathJax_Display {
font-size: 1.25em;
}

#toggle-theme {
border-width: 0;
line-height: 4em;
padding-left: 1em;
padding-right: 1em;
background: var(--bg-block);
color: var(--clr-code);
}

#toggle-theme:hover {
cursor: pointer;
}

img {
width: 100%;
}

@media only screen and (max-width: 768px) {
#toggle-theme {
width: 100%;
text-align: center;
}
}

@media only screen and (min-width: 768px) {
#toggle-theme {
position: absolute;
top: var(--mg);
right: var(--mg);
border-radius: 5px;
}
}

.theme-transition {
transition: all 0.5s;
}

.status {
width: 100%;
}

@media only screen and (max-width: 768px) {
ul {
padding-left: .5em;
margin-left: 0;
}
li {
padding-left: 0;
margin-left: 0;
}
}
/*]]>*/-->


dark theme




Luca's literate Emacs config



Table of Contents







1. Introduction






1.1. This file




This file (readme.org) is my literate emacs configuration.
Every time I save the file, the code blocks get tangled.
By default, they get tangled (in sequence) to ./init.el.
Some blocks override this default (e.g. see the section early-init.el).


This file also is exported to HTML, it can be viewed here.
See this section for my configuration.
You can access my blog at the same website.





1.2. Why vanilla?




I used DOOM emacs for an year and I was an happy user.
One day I woke up with the wish to understand Emacs a little more.


After about a week (12/01/2021) I had restored my configuration and in the process I understood better concepts such as:



  • hooks

  • minor and major modes

  • advices


It is still a long journey but I am glad I started it.






1.3. Why literate?




Having your configuration in org-mode has some benefits and some drawbacks.
It adds a layer of abstraction between me and my init.el file, is it worth it?


The main drawback is that it can happen that the org-mode file has a mistake and tangles
an incorrect init.el file. In that case you can't use your nice bindings but you are
thrown in barebones emacs and you have to C-x C-f your way to the init.el and run
check-parens.


Another drawback is that a big configuration can be slow to tangle and tangling on save can block emacs.
See this section for a solution to this drawback.


Let's consider some of the benefits:



  • I can export this file to HTML (here)

  • People can read this file on Github (here)

  • I can comfortably document my configuration (and not from within comments), include links, sh code blocks, etc.

  • I can organize my configuration blocks in sections, easily disable some headings with COMMENT






1.4. Modules




I tangle this file with the function lc/tangle-config, you can read source code in this section.
Every time I save this .org file, it is tangled to multiple .el files.


I achieve that by means of this file's "local variables", which I put at the end of the .org file:



# Local Variables:

# eval: (add-hook 'after-save-hook (lambda ()(progn (lc/org-add-ids-to-headlines-in-file) (lc/tangle-config))) nil t)
# End:


To design modules, I look at blocks in my config that I would like to toggle on and off.
For example if I am on an iPad I may want to not load anything related to Python or vterm.


I assign org properties to each heading. These are determine which .el file they will be britten to.
For example the header of the section concerning lsp-mode has the following properties:



:PROPERTIES:

:CUSTOM_ID: h:6BC08822-D2B3-4BE9-9EBE-C42F89F0E688
:header-args: :emacs-lisp :tangle ./lisp/init-prog-lsp.el
:END:


All subheadings under it will "inherit those properties and will be tangled to the same file.
We also need to write some emacs-lisp at the end of the tanged file to "provide" those modules.
Here an example of one of these "footer" headers.


I then have a lean init.el (written in this section) which I use to control which modules I want to use in my session.
On my main computer I typically want to enable all of them.





1.5. How can I fork it?




I guess one could fork this repo and use this org file as template.
You can duplicate this file, give it another name and tangle that file to your init.el.


I would start with a "small" configuration, just with the "core" functionalities.
For example the Startup, the Package manager and general sections would be a good starting point.


Then, you can start importing "sections" you are curious about, for example Completion framework .
You could also COMMENT all headings and uncomment only those which are interesting to you.
You could find the org-toggle-comment command useful, after selecting all headings in the file.





1.6. Structure of this configuration




  • In the second section some optimization of startup time, mostly stolen from smart people.

  • In the third section we bootstrap straight and use-package, our package managers

  • In the fourth section we configure emacs with sane defaults and extend some its core features (e.g. help-mode)

  • In the fifth section we set up general, which we use to manage our keybindings and lazy loading of packages. Afterwards we configure evil, for modal editing.

  • In the sixth section the invaluable org-mode with several extensions

  • The remaining sections declare my personal configuration of UI and core packages, leveraging the great tools described in this list.






1.7. Notable sections




I am particularly proud of some sections in this configuration, because of one or more of these reasons:



  • They improve my productivity considerably

  • They are non-standard solutions (or at least hard to find online)

  • They are particularly fine-tuned to my workflow


Here they are, without any ranking:








2. early-init.el and init.el






2.1. early-init.el




Taken from DOOM's early init



;;; early-init.el --- Early Init File -*- lexical-binding: t; no-byte-compile: t -*-

;; NOTE: early-init.el is now generated from readme.org. Please edit that file instead

;; Defer garbage collection further back in the startup process
(setq gc-cons-threshold most-positive-fixnum
gc-cons-percentage 0.6)

;; In Emacs 27+, package initialization occurs before `user-init-file' is
;; loaded, but after `early-init-file'. Doom handles package initialization, so
;; we must prevent Emacs from doing it early!
(setq package-enable-at-startup nil)
;; Do not allow loading from the package cache (same reason).
(setq package-quickstart nil)

;; Prevent the glimpse of un-styled Emacs by disabling these UI elements early.
(push '(menu-bar-lines . 0) default-frame-alist)
(push '(tool-bar-lines . 0) default-frame-alist)
(push '(vertical-scroll-bars) default-frame-alist)

;; Resizing the Emacs frame can be a terribly expensive part of changing the
;; font. By inhibiting this, we easily halve startup times with fonts that are
;; larger than the system default.
(setq frame-inhibit-implied-resize t)

;; Disable GUI elements
(menu-bar-mode -1)
(tool-bar-mode -1)
(scroll-bar-mode -1)
(setq inhibit-splash-screen t)
(setq use-file-dialog nil)

;; Prevent unwanted runtime builds in gccemacs (native-comp); packages are
;; compiled ahead-of-time when they are installed and site files are compiled
;; when gccemacs is installed.
(setq comp-deferred-compilation nil)

;;; early-init.el ends here






2.2. init.el: startup optimization




Taken from DOOM's init



;;; init.el --- Personal configuration file -*- lexical-binding: t; no-byte-compile: t; -*-

;; NOTE: init.el is now generated from readme.org. Please edit that file instead

;; `file-name-handler-alist' is consulted on every `require', `load' and various
;; path/io functions. You get a minor speed up by nooping this. However, this
;; may cause problems on builds of Emacs where its site lisp files aren't
;; byte-compiled and we're forced to load the *.el.gz files (e.g. on Alpine)
(unless (daemonp)
(defvar doom--initial-file-name-handler-alist file-name-handler-alist)
(setq file-name-handler-alist nil)
;; Restore `file-name-handler-alist' later, because it is needed for handling
;; encrypted or compressed files, among other things.
(defun doom-reset-file-handler-alist-h ()
;; Re-add rather than `setq', because changes to `file-name-handler-alist'
;; since startup ought to be preserved.
(dolist (handler file-name-handler-alist)
(add-to-list 'doom--initial-file-name-handler-alist handler))
(setq file-name-handler-alist doom--initial-file-name-handler-alist))
(add-hook 'emacs-startup-hook #'doom-reset-file-handler-alist-h)
(add-hook 'after-init-hook '(lambda ()
;; restore after startup
(setq gc-cons-threshold 16777216
gc-cons-percentage 0.1)))
)
;; Ensure Doom is running out of this file's directory
(setq user-emacs-directory (file-truename (file-name-directory load-file-name)))






2.3. init.el: load modules




;; (add-to-list 'load-path "~/.emacs.d/lisp/")

(add-to-list 'load-path (expand-file-name "lisp" user-emacs-directory))

(let ((file-name-handler-alist nil)
(gc-cons-threshold 100000000))
(require 'init-core)
(require 'init-ui-extra)
(require 'init-org-roam)
(require 'init-org-export)
(require 'init-prog-vterm)
(require 'init-prog-nix)
(require 'init-prog-lsp)
(require 'init-prog-python)
;; (require 'init-prog-jupyter)
(require 'init-prog-elisp)
(require 'init-prog-markdown)
;; (require 'init-prog-r)
;; (require 'init-prog-clojure)
(require 'init-prog-tree-sitter)
(require 'init-extra-focus)
(require 'init-extra-web)
;; (require 'init-extra-rss)
;; (require 'init-extra)
)

;;; init.el ends here







3. Package manager






3.1. bootstrap straight and straight-use-package




Some rules/conventions:



  • Prefer :init to :custom. Prefer multiple setq expressions to one.

  • Default to :defer t, use :demand to force loading

  • When packages do not require installation e.g. dired, we need :straight (:type built-in)

  • If you specify :commands, they will be autoloaded and the package will be loaded when the commands are first executed

    • If you use :general and bind commands to keys it will automatically load the package on first invokation




NOTE: if you change a package recipe from melpa to github in a use-package block but
that package is used as a dependency is used in a previous use-package block with a melpa
recipe, you will get a warning. Just make sure to declare the "base" package with the github recipe first.


(setq straight-use-package-by-default t)

(setq straight-vc-git-default-clone-depth 1)
(setq straight-recipes-gnu-elpa-use-mirror t)
;; (setq straight-check-for-modifications '(check-on-save find-when-checking))
(setq straight-check-for-modifications nil)
(setq use-package-always-defer t)
(defvar bootstrap-version)
(let* ((straight-repo-dir
(expand-file-name "straight/repos" user-emacs-directory))
(bootstrap-file
(concat straight-repo-dir "/straight.el/bootstrap.el"))
(bootstrap-version 5))
(unless (file-exists-p bootstrap-file)
(shell-command
(concat
"mkdir -p " straight-repo-dir " && "
"git -C " straight-repo-dir " clone "
"https://github.com/raxod502/straight.el.git && "
"git -C " straight-repo-dir " checkout 2d407bc")))
(load bootstrap-file nil 'nomessage))
(straight-use-package 'use-package)
;; This is a variable that has been renamed but straight still refers when
;; doing :sraight (:no-native-compile t)
(setq comp-deferred-compilation-black-list nil)





3.2. straight lockfile




We can run M-x straight-freeze-versions to write the file straight/versions/default.el.
The content of the file can then be kept in a code block, under version control.
The code block can then be tangle again to straight/versions/default.el.
We can then restore package versions using M-x straight-thaw-versions.


(("Emacs-wgrep" . "f9687c28bbc2e84f87a479b6ce04407bb97cfb23")

("ace-window" . "0577c426a9833ab107bab46c60d1885c611b2fb9")
("all-the-icons-dired" . "5e9b097f9950cc9f86de922b07903a4e5fefc733")
("all-the-icons.el" . "6d48bc9e970ab559bc35a125c55fd83732595706")
("annalist.el" . "134fa3f0fb91a636a1c005c483516d4b64905a6d")
("avy" . "ba5f035be33693d1a136a5cbeedb24327f551a92")
("blacken" . "880cf502198753643a3e2ccd4131ee6973be2e8a")
("bui.el" . "f3a137628e112a91910fd33c0cff0948fa58d470")
("centaur-tabs" . "5860a5c40c2318797f1274ea4c6907ae77ea1ec9")
("centered-cursor-mode.el" . "4093821cc9759ca5a3c6e527d4cc915fc3a5ad74")
("cfrs" . "c1f639d7bfd3e728cf85dbe224b06a4be76158f4")
("consult" . "0940ca016531f3412003c231b476e5023a510ff9")
("corfu" . "2f5e15add5e1fdd20ff83d647e460cd8f85327a5")
("csv-mode" . "a4ad2e59d4039bf3b1bdbebdfab21c122ad5b487")
("dap-mode" . "76cad34de8984f57c2b1e374e9c985cc7ec8dad0")
("darkroom" . "27b928a6e91c3207742180f7e209bae754c9c1fe")
("dash.el" . "da167c51e9fd167a48d06c7c0ee8e3ac7abd9718")
("diff-hl" . "6fa3af0843093f44e028584a93eef091ec7e79d2")
("dired-hacks" . "7c0ef09d57a80068a11edc74c3568e5ead5cc15a")
("dired-hide-dotfiles" . "6a379f23f64045f5950d229254ce6f32dbbf5364")
("dired-single" . "b254f9b7bfc96a5eab5760a56811f2872d2c590a")
("docker-tramp.el" . "7bfbb55417e7d2aac53adf92cb0e3fd329c495c1")
("doom-modeline" . "5f30d231176186cbe5206aa133f91cea967717d9")
("el-get" . "9353309744e4f8a7c9b1adf22ec99536fb2146b0")
("eldoc" . "37e51a5810c496356cdb7107d14c2b7eb2dbdf0b")
("elisp-refs" . "c06aec4486c034d0d4efae98cb7054749f9cc0ec")
("elisp-tree-sitter" . "48b06796a3b2e76ce004972d929de38146eafaa0")
("emacs-async" . "0d52411d3accc3e11a2c64838703a8ce9755c77c")
("emacs-bind-map" . "bf4181e3a41463684adfffc6c5c305b30480e30f")
("emacs-dashboard" . "1bb5c43b6be65f72c2ff3ab948697c902458a32f")
("emacs-hide-mode-line" . "bc5d293576c5e08c29e694078b96a5ed85631942")
("emacs-libvterm" . "a940dd2ee8a82684860e320c0f6d5e15d31d916f")
("emacs-python-pytest" . "b603c5c7f21d351364deeb78e503d3a54d08a152")
("emacs-undo-fu" . "ab8bc10e424bccc847800c31ab41888db789d55d")
("emacs-which-key" . "9f64733e4ac563c0cda3685acf4e1c2cf600319b")
("emacs-winum" . "c5455e866e8a5f7eab6a7263e2057aff5f1118b9")
("emacsmirror-mirror" . "a122c213c5b3ff1bb7fd7995f07b7a18334a19e7")
("emacsql" . "c82a0e6b4d256a3743b718cfb640fa9efc045f6e")
("embark" . "c9b26c2e18f01ae401df6a69b7a0c1a6bc44b90c")
("envrc" . "456c4100de41d2cb50813058a9e727b6e83c5d1e")
("epl" . "78ab7a85c08222cd15582a298a364774e3282ce6")
("eros" . "dd8910279226259e100dab798b073a52f9b4233a")
("evil" . "b5c6950f65e2cde92bd978ec2bc98e67820d91a0")
("evil-cleverparens" . "8c45879d49bfa6d4e414b6c1df700a4a51cbb869")
("evil-collection" . "e6be41bed7b4399db116038c7f0bf2f484065b48")
("evil-goggles" . "08a22058fd6a167f9f1b684c649008caef571459")
("evil-iedit-state" . "93e4cbfcee802adbb9dd0ebd5836fea4fa932849")
("evil-indent-plus" . "0c7501e6efed661242c3a20e0a6c79a6455c2c40")
("evil-lisp-state" . "3c65fecd9917a41eaf6460f22187e2323821f3ce")
("evil-mc" . "246aecc17481dd23c172a9b845f02a9d9e322c7f")
("evil-nerd-commenter" . "63baf2d1c796edd11bbec5fe1dee711173d4155d")
("evil-org-mode" . "a9706da260c45b98601bcd72b1d2c0a24a017700")
("evil-snipe" . "a79177df406a79b4ffa25743c752f21363bba1cc")
("evil-surround" . "282a975bda83310d20a2c536ac3cf95d2bf188a5")
("exec-path-from-shell" . "3a8d97c096c2c5714b667130fd8a80d5622ee067")
("f.el" . "50af874cd19042f17c8686813d52569b1025c76a")
("flycheck" . "784f184cdd9f9cb4e3dbb997c09d93e954142842")
("gcmh" . "0089f9c3a6d4e9a310d0791cf6fa8f35642ecfd9")
("general.el" . "9651024e7f40a8ac5c3f31f8675d3ebe2b667344")
("git-timemachine" . "3381797bcbf906b18dff654a2361032d2d01b4a3")
("gnu-elpa-mirror" . "ebddf266c5234580c9458a139b419c5db1374793")
("goto-chg" . "a7c69fa6a33774ee8ca759a064845a88483a4995")
("helpful" . "f865f17ad04cd270685187b0a5331ec8eb06e541")
("highlight-indent-guides" . "cf352c85cd15dd18aa096ba9d9ab9b7ab493e8f6")
("hl-todo" . "42f744ffb513cf2b95517144c64dbf3fc69f711a")
("ht.el" . "c4c1be487d6ecb353d07881526db05d7fc90ea87")
("hydra" . "9e9e00cb240ea1903ffd36a54956b3902c379d29")
("iedit" . "3c7159a107b01b8f740ced72f10351b10bb69784")
("inheritenv" . "7e4c8b0d0a43b6f1c6c4d6dbd2f3bf5ce7f20067")
("isearch-mb" . "27c19935dd7f067c9feb9a5fda17844c39749346")
("kind-icon" . "6e0e0c5c2f846685861ef6c157044b1a55572359")
("let-alist" . "592553db5929b54db40af0df90c5add0aaca045b")
("lsp-mode" . "a82a4fa3467ec918273ab65d48c5c7d2dbfaec74")
("lsp-pyright" . "d428dbcf1802fbe147271c8dc74b073bd9cd7403")
("lsp-treemacs" . "72d367757a89453a712f6ba1df9b6e789ece2bbd")
("lsp-ui" . "21ce926eedd41ef305c2d89412506ce59b1a7eac")
("magit" . "2e73b66c2980abb9211d9881a8710c8ac5a33184")
("marginalia" . "9229d88ae4757f3439e81f51799758c009838cb4")
("markdown-mode" . "4469553a7395359e96b8796e1fac4de73cb6ccc4")
("melpa" . "4e3d46311b4d15314b6d1a0d5ff95c5f7e366223")
("modus-themes" . "fe5e1f7a16bae0df0cc305dcb3e5be32cf10f0b5")
("nix-mode" . "3d04d92d9c3896d07bc9fed7e4f40032025fbe7b")
("no-littering" . "665e324abb690fb50e9d255bc656eb12bb83b0c6")
("ob-async" . "9aac486073f5c356ada20e716571be33a350a982")
("olivetti" . "a31ac05a161a91fe5c157930b62a6c07037982ee")
("orderless" . "1ccf74ffdbb0dd34caa63022e92f947c09c49c86")
("org" . "b8656a2cef3d5da7270e391222d8139fc042edd5")
("org-appear" . "a4d10fc346ba14f487eb7aa95761b9295089ba55")
("org-fragtog" . "15861261a437aca2ec858317de71603d2957b423")
("org-reverse-datetree" . "e7a7109e4c34811d471bf685b710234564a556f6")
("org-roam" . "679ef6ef001fd1a69b691108178721aa913e7f0f")
("org-superstar-mode" . "03be6c0a3081c46a59b108deb8479ee24a6d86c0")
("org-tree-slide" . "917612a0d1593de533b7bf0a2792d7e37bb2ca3d")
("paredit" . "8330a41e8188fe18d3fa805bb9aa529f015318e8")
("persistent-scratch" . "57221e5fdff22985c0ea2f3e7c282ce823ea5932")
("persp-projectile" . "4e374d7650c7e041df5af5ac280a44d4a4ec705a")
("perspective-el" . "d8211a80fbc2cc0d9e163ef6a3e1d0a693b4e00e")
("pfuture" . "bde5b06795e3e35bfb2bba4c34b538d506a0856e")
("pkg-info" . "76ba7415480687d05a4353b27fea2ae02b8d9d61")
("posframe" . "80cb98aff695a102772cc414d96611bdaf96f00e")
("powerline" . "390a95fe5b71cfc20e18d034b4b35b5c159a83fc")
("projectile" . "24de2940a8a1f46a7715175a66be67733f1c8fa8")
("python-mode" . "dcb376044d020dfe30f8e4273e61863b7d9615ce")
("rainbow-delimiters" . "a32b39bdfe6c61c322c37226d66e1b6d4f107ed0")
("restart-emacs" . "1607da2bc657fe05ae01f7fdf26f716eafead02c")
("s.el" . "08661efb075d1c6b4fa812184c1e5e90c08795a9")
("shrink-path.el" . "c14882c8599aec79a6e8ef2d06454254bb3e1e41")
("smartparens" . "f59a40d54f35299007c396bd667ce3e9ec4714e3")
("spinner" . "34905eae12a236753fa88abc831eff1e41e8576e")
("straight.el" . "af5437f2afd00936c883124d6d3098721c2d306c")
("svg-lib" . "d736ea09d6aa7064ff974f5bee0f3aadc0d157d4")
("toml-mode.el" . "f6c61817b00f9c4a3cab1bae9c309e0fc45cdd06")
("transient" . "45241225cf6bec864964191c0b3dc8bfad989723")
("transpose-frame" . "12e523d70ff78cc8868097b56120848befab5dbc")
("tree-sitter-langs" . "3c0c82f9fb0a796f5ebd7e1e4c89f13d5ab6ef58")
("treemacs" . "deb7f2cd9eb06960798edd7393df2602902ed071")
("use-package" . "a7422fb8ab1baee19adb2717b5b47b9c3812a84c")
("vertico" . "9a3cdb754b018bd43a34fec464d4a88eae167c0e")
("vterm-toggle" . "ea0bfeaa200d9ed02b51bbbd83222657b02637e7")
("with-editor" . "36d36957628621e8340f755b22082e1f4ed8e2d7")
("xwwp" . "f67e070a6e1b233e60274deb717274b000923231")
("yaml-mode" . "a79d2a7b9281f8c56f461d717b1ba40fc58e22fd")
("yasnippet" . "5cbdbf0d2015540c59ed8ee0fcf4788effdf75b6"))
:beta





3.3. Enable use-package statistics




If you'd like to see how many packages you've loaded, what stage of initialization they've reached, and how much aggregate time they've spent (roughly), you can enable use-package-compute-statistics after loading use-package but before any use-package forms, and then run the command M-x use-package-report to see the results. The buffer displayed is a tabulated list. You can use S in a column to sort the rows based on it.


(setq use-package-compute-statistics t)



From the report:



  • evil 0.56

  • embark 0.25

  • projectile 0.18






4. Emacs






4.1. Sane defaults




Inspired by https://github.com/natecox/dotfiles/blob/master/emacs/emacs.d/nathancox.org


To debug a LISP function use debug-on-entry. You step in with d and over with e


(use-package emacs

:init
(setq inhibit-startup-screen t
initial-scratch-message nil
sentence-end-double-space nil
ring-bell-function 'ignore
frame-resize-pixelwise t)

(setq user-full-name "Luca Cambiaghi"
user-mail-address "[email protected]")

(setq read-process-output-max (* 1024 1024)) ;; 1mb

;; always allow 'y' instead of 'yes'.
(defalias 'yes-or-no-p 'y-or-n-p)

;; default to utf-8 for all the things
(set-charset-priority 'unicode)
(setq locale-coding-system 'utf-8
coding-system-for-read 'utf-8
coding-system-for-write 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(set-selection-coding-system 'utf-8)
(prefer-coding-system 'utf-8)
(setq default-process-coding-system '(utf-8-unix . utf-8-unix))

;; write over selected text on input... like all modern editors do
(delete-selection-mode t)

;; enable recent files mode.
(recentf-mode t)
(setq recentf-exclude `(,(expand-file-name "straight/build/" user-emacs-directory)
,(expand-file-name "eln-cache/" user-emacs-directory)
,(expand-file-name "etc/" user-emacs-directory)
,(expand-file-name "var/" user-emacs-directory)))

;; don't want ESC as a modifier
(global-set-key (kbd "<escape>") 'keyboard-escape-quit)

;; Don't persist a custom file, this bites me more than it helps
(setq custom-file (make-temp-file "")) ; use a temp file as a placeholder
(setq custom-safe-themes t) ; mark all themes as safe, since we can't persist now
(setq enable-local-variables :all) ; fix =defvar= warnings

;; stop emacs from littering the file system with backup files
(setq make-backup-files nil
auto-save-default nil
create-lockfiles nil)

;; follow symlinks
(setq vc-follow-symlinks t)

;; don't show any extra window chrome
(when (window-system)
(tool-bar-mode -1)
(toggle-scroll-bar -1))

;; enable winner mode globally for undo/redo window layout changes
(winner-mode t)

(show-paren-mode t)

;; less noise when compiling elisp
(setq byte-compile-warnings '(not free-vars unresolved noruntime lexical make-local))
(setq native-comp-async-report-warnings-errors nil)
(setq load-prefer-newer t)

;; clean up the mode line
(display-time-mode -1)
(setq column-number-mode t)

;; use common convention for indentation by default
(setq-default indent-tabs-mode t)
(setq-default tab-width 2)

;; Enable indentation+completion using the TAB key.
;; Completion is often bound to M-TAB.
(setq tab-always-indent 'complete)
)






4.2. custom variables




(use-package emacs

:init
(setq lc/is-ipad ( ;; <
> (length (shell-command-to-string "uname -a | grep iPad")) 0))

(setq lc/is-windows (eq system-type 'windows-nt))

(defcustom lc/default-font-family "fira code"
"Default font family"
:type 'string
:group 'lc)

(defcustom lc/variable-pitch-font-family "Sans Serif" ;; "cantarell" ;;
"Variable pitch font family"
:type 'string
:group 'lc)

(defcustom lc/laptop-font-size
(if lc/is-windows 100 150)
"Font size used for laptop"
:type 'int
:group 'lc)

(defcustom lc/theme nil
"Current theme (light or dark)"
:type 'symbol
:options '(light dark)
:group 'lc)

;; (setq lc/is-low-power (string= (system-name) "pntk"))


;; (setq lc/is-slow-ssh (string= (getenv "IS_TRAMP") "true"))

)






4.3. Font




(use-package emacs

:init
(defun lc/get-font-size ()
"font size is calculated according to the size of the primary screen"
(let* (;; (command "xrandr | awk '/primary/{print sqrt( ($(nf-2)/10)^2 + ($nf/10)^2 )/2.54}'")
(command "osascript -e 'tell application \"finder\" to get bounds of window of desktop' | cut -d',' -f3")
(screen-width (string-to-number (shell-command-to-string command)))) ;;<
(if (> screen-width 2560) lc/laptop-font-size lc/laptop-font-size)))

;; Main typeface
(set-face-attribute 'default nil :font lc/default-font-family :height (lc/get-font-size))
;; Set the fixed pitch face (monospace)
(set-face-attribute 'fixed-pitch nil :font lc/default-font-family)
;; Set the variable pitch face
(set-face-attribute 'variable-pitch nil :font lc/variable-pitch-font-family)
)





4.4. Zoom




(use-package emacs

:init
(global-set-key (kbd "C-=") 'text-scale-increase)
(global-set-key (kbd "C--") 'text-scale-decrease)
)





4.5. macOS




(use-package emacs

:init
(defun lc/is-macos ()
(and (eq system-type 'darwin)
(= 0 (length (shell-command-to-string "uname -a | grep iPad")))))
(when (lc/is-macos)
(setq mac-command-modifier 'super) ; command as super
(setq mac-option-modifier 'meta) ; alt as meta
(setq mac-control-modifier 'control)
)
;; when on emacs-mac
(when (fboundp 'mac-auto-operator-composition-mode)
(mac-auto-operator-composition-mode) ;; enables font ligatures
(global-set-key [(s c)] 'kill-ring-save)
(global-set-key [(s v)] 'yank)
(global-set-key [(s x)] 'kill-region)
(global-set-key [(s q)] 'kill-emacs)
)
)





4.6. Garbage collector magic hack




Used by DOOM to manage garbage collection



(use-package gcmh

:demand
:config
(gcmh-mode 1))





4.7. helpful




  (use-package helpful

:after evil
:init
(setq evil-lookup-func #'helpful-at-point)
:bind
([remap describe-function] . helpful-callable)
([remap describe-command] . helpful-command)
([remap describe-variable] . helpful-variable)
([remap describe-key] . helpful-key))





4.8. eldoc




  (use-package eldoc

:hook (emacs-lisp-mode cider-mode))





4.9. exec path from shell




  (use-package exec-path-from-shell

;; :if (memq window-system '(mac ns))
:if (lc/is-macos)
:hook (emacs-startup . (lambda ()
(setq exec-path-from-shell-arguments '("-l")) ; removed the -i for faster startup
(exec-path-from-shell-initialize)))
;; :config
;; (exec-path-from-shell-copy-envs
;; '("GOPATH" "GO111MODULE" "GOPROXY"
;; "NPMBIN" "LC_ALL" "LANG" "LC_TYPE"
;; "SSH_AGENT_PID" "SSH_AUTH_SOCK" "SHELL"
;; "JAVA_HOME"))
)





4.10. no littering




(use-package no-littering

:demand
:config
(with-eval-after-load 'recentf
(add-to-list 'recentf-exclude no-littering-var-directory)
(add-to-list 'recentf-exclude no-littering-etc-directory))
)





4.11. server mode




(use-package emacs

:init
(unless (and (fboundp 'server-running-p)
(server-running-p))
(server-start)))





4.12. Auto-pair parenthesis




(use-package emacs

:hook
((org-jupyter-mode . (lambda () (lc/add-local-electric-pairs '())))
(org-mode . (lambda () (lc/add-local-electric-pairs '(;(?= . ?=)
(?~ . ?~))))))
:init
;; auto-close parentheses
(electric-pair-mode +1)
(setq electric-pair-preserve-balance nil)
;; mode-specific local-electric pairs
(defconst lc/default-electric-pairs electric-pair-pairs)
(defun lc/add-local-electric-pairs (pairs)
"Example usage:
(add-hook 'jupyter-org-interaction-mode '(lambda () (set-local-electric-pairs '())))
"
(setq-local electric-pair-pairs (append lc/default-electric-pairs pairs))
(setq-local electric-pair-text-pairs electric-pair-pairs))

;; disable auto pairing for < >
(add-function :before-until electric-pair-inhibit-predicate
(lambda (c) (eq c ?< ;; >
)))
)



You can use below block to print the value of electric-pair-pairs



(mapcar (lambda (elm) (char-to-string (car elm))) electric-pair-pairs)






4.13. Rename file




(use-package emacs

:init
(defun lc/rename-current-file ()
"Rename the current visiting file and switch buffer focus to it."
(interactive)
(let ((new-filename (lc/expand-filename-prompt
(format "Rename %s to: " (file-name-nondirectory (buffer-file-name))))))
(if (null (file-writable-p new-filename))
(user-error "New file not writable: %s" new-filename))
(rename-file (buffer-file-name) new-filename 1)
(find-alternate-file new-filename)
(message "Renamed to and now visiting: %s" (abbreviate-file-name new-filename))))
(defun lc/expand-filename-prompt (prompt)
"Return expanded filename prompt."
(expand-file-name (read-file-name prompt)))
)






4.14. xref




(use-package xref

:straight (:type built-in)
:init
(setq xref-prompt-for-identifier nil) ;; always find references of symbol at point
;; configured in consult
;; (setq xref-show-definitions-function #'xref-show-definitions-completing-read)
;; (setq xref-show-xrefs-function #'xref-show-definitions-buffer) ; for grep and the like
;; (setq xref-file-name-display 'project-relative)
;; (setq xref-search-program 'grep)
)





4.15. Don't close windows on escape




Sometimes ESC kills my window layout. This advice prevents that from happening.



(use-package emacs

:init
(defadvice keyboard-escape-quit
(around keyboard-escape-quit-dont-close-windows activate)
(let ((buffer-quit-function (lambda () ())))
ad-do-it))
)






4.16. emacs extra packages








5. Keybindings






5.1. general




In this block we load general and define bindings for generic commands e.g. find-file.
The commands provided by packages should be binded in the use-package block, thanks to the :general keyword.
NOTE: We need to load general before evil, otherwise the :general keyword in the use-package blocks won't work.


(use-package general

:demand t
:config
(general-evil-setup)

(general-create-definer lc/leader-keys
:states '(normal insert visual emacs)
:keymaps 'override
:prefix "SPC"
:global-prefix "C-SPC")

(general-create-definer lc/local-leader-keys
:states '(normal visual)
:keymaps 'override
:prefix ","
:global-prefix "SPC m")

(general-nmap
:states 'normal
"gD" '(xref-find-references :wk "references")
)

(lc/leader-keys
"SPC" '(execute-extended-command :which-key "execute command")
"`" '((lambda () (interactive) (switch-to-buffer (other-buffer (current-buffer) 1))) :which-key "prev buffer")
"<escape>" 'keyboard-escape-quit

";" '(eval-expression :which-key "eval sexp")

"b" '(:ignore t :which-key "buffer")
"br" 'revert-buffer
;; "bs" '((lambda () (interactive)
;; (pop-to-buffer "*scratch*"))
;; :wk "scratch")
"bd" 'kill-current-buffer

"c" '(:ignore t :which-key "code")

"f" '(:ignore t :which-key "file")
"fD" '((lambda () (interactive) (delete-file (buffer-file-name))) :wk "delete")
"ff" 'find-file
"fs" 'save-buffer
"fR" '(lc/rename-current-file :wk "rename")

"g" '(:ignore t :which-key "git")
;; keybindings defined in magit

"h" '(:ignore t :which-key "describe")
"he" 'view-echo-area-messages
"hf" 'describe-function
"hF" 'describe-face
"hl" 'view-lossage
"hL" 'find-library
"hm" 'describe-mode
"hk" 'describe-key
"hK" 'describe-keymap
"hp" 'describe-package
"hv" 'describe-variable

"k" '(:ignore t :which-key "kubernetes")
;; keybindings defined in kubernetes.el

"o" '(:ignore t :which-key "org")
;; keybindings defined in org-mode

;; "p" '(:ignore t :which-key "project")
;; keybindings defined in projectile

"s" '(:ignore t :which-key "search")
;; keybindings defined in consult

"t" '(:ignore t :which-key "toggle")
"t d" '(toggle-debug-on-error :which-key "debug on error")
"t l" '(display-line-numbers-mode :wk "line numbers")
"t w" '((lambda () (interactive) (toggle-truncate-lines)) :wk "word wrap")
;; "t +" '(lc/increase-font-size :wk "+ font")
;; "t -" '(lc/decrease-font-size :wk "- font")
;; "t +" 'text-scale-increase
;; "t -" 'text-scale-decrease
;; "t 0" '(lc/reset-font-size :wk "reset font")

"u" '(universal-argument :wk "universal")

"w" '(:ignore t :which-key "window")
"wl" 'windmove-right
"wh" 'windmove-left
"wk" 'windmove-up
"wj" 'windmove-down
"wr" 'winner-redo
"wd" 'delete-window
"w=" 'balance-windows-area
"wD" 'kill-buffer-and-window
"wu" 'winner-undo
"wr" 'winner-redo
"wm" '(delete-other-windows :wk "maximize")

"x" '(:ignore t :which-key "browser")
;; keybindings defined in xwwp
)

(lc/local-leader-keys
:states 'normal
"d" '(:ignore t :which-key "debug")
"e" '(:ignore t :which-key "eval")
"t" '(:ignore t :which-key "test")))






5.2. evil






5.2.1. evil mode




Best VIM reference: https://countvajhula.com/2021/01/21/vim-tip-of-the-day-a-series/


Search tricks:




  • * / # to go to next/prev occurence of symbol under point


  • / starts a search, use n / N to go to next/prev

  • Use the gn noun to, for example, change next match with cgn


Some interesting vim nouns:



_

first character in the line (synonym to ^)

g_

last character on the line (synonym to $)


Marks:



ma

mark a position in buffer and save it to register a

'a

go to mark a

mA

mark position and filename [

]'

go to next mark

''

go back to previous mark (kept track automatically)

g;

go to previous change location

gi

go back to insert mode where you left off



C-o

jump (out) to previous position (useful after gd)

C-i

jump (in) to previous position


Macros:



qq

record macro q

@q

execute macro q


Registers:



"ayio

save object in register a "

"ap

paste object in register a "

  • Macros are saved in registers so you can simply "qp and paste your macro!! "




NOTE: I inserted the above quotes because the single double quotes were breaking my VIM object detection
in the rest of the file


(use-package evil

:demand
:general
(lc/leader-keys
"wv" 'evil-window-vsplit
"ws" 'evil-window-split)
:init
(setq evil-want-integration t)
(setq evil-want-keybinding nil)
(setq evil-want-C-u-scroll t)
(setq evil-want-C-i-jump t)
(setq evil-want-Y-yank-to-eol t)
;; (setq evil-respect-visual-line-mode t)
(setq evil-undo-system 'undo-fu)
(setq evil-search-module 'evil-search) ;; enables gn
;; move to window when splitting
(setq evil-split-window-below t)
(setq evil-vsplit-window-right t)
;; (setq-local evil-scroll-count 0)
(setq evil-auto-indent nil)
:config
(evil-mode 1)
(define-key evil-insert-state-map (kbd "C-g") 'evil-normal-state)
(define-key evil-motion-state-map "_" 'evil-end-of-line)
(define-key evil-motion-state-map "0" 'evil-beginning-of-line)
(evil-set-initial-state 'messages-buffer-mode 'normal)
(evil-set-initial-state 'dashboard-mode 'normal)
;; don't move cursor after ==
(defun lc/evil-dont-move-cursor (orig-fn &rest args)
(save-excursion (apply orig-fn args)))
(advice-add 'evil-indent :around #'lc/evil-dont-move-cursor)
)





5.2.2. evil-collection




(use-package evil-collection

:after evil
:demand
:init
(setq evil-collection-magit-use-z-for-folds nil)
:config
(evil-collection-init))





5.2.3. eval operator




This section provides a custom eval operator, accessible with gr.
This gives you super powers when coupled with custom text objects (provided by evil-indent-plus and )


For example:




  • grab evals the form at point


  • grad evals the top-level form (e.g. use-package blocks or functions)


  • grak evals the function in python


  • grr evals the line


(use-package evil

:config
(defcustom evil-extra-operator-eval-modes-alist
'((emacs-lisp-mode eros-eval-region)
;; (scheme-mode geiser-eval-region)
(clojure-mode cider-eval-region)
(jupyter-python jupyter-eval-region) ;; when executing in src block
(python-mode python-shell-send-region) ;; when executing in org-src-edit mode
)
"Alist used to determine evil-operator-eval's behaviour.
Each element of this alist should be of this form:
(MAJOR-MODE EVAL-FUNC [ARGS...])
MAJOR-MODE denotes the major mode of buffer. EVAL-FUNC should be a function
with at least 2 arguments: the region beginning and the region end. ARGS will
be passed to EVAL-FUNC as its rest arguments"
:type '(alist :key-type symbol)
:group 'evil-extra-operator)

(evil-define-operator evil-operator-eval (beg end)
"Evil operator for evaluating code."
:move-point nil
(interactive "<r>")
(let* ((mode (if (org-in-src-block-p) (intern (car (org-babel-get-src-block-info))) major-mode))
(ele (assoc mode evil-extra-operator-eval-modes-alist))
(f-a (cdr-safe ele))
(func (car-safe f-a))
(args (cdr-safe f-a)))
(if (fboundp func)
(apply func beg end args)
(eval-region beg end t))))

(define-key evil-motion-state-map "gr" 'evil-operator-eval)

)






5.2.4. evil-goggles




(use-package evil-goggles

:after evil
:demand
:init
(setq evil-goggles-duration 0.05)
:config
(push '(evil-operator-eval
:face evil-goggles-yank-face
:switch evil-goggles-enable-yank
:advice evil-goggles--generic-async-advice)
evil-goggles--commands)
(evil-goggles-mode)
(evil-goggles-use-diff-faces)
)





5.2.5. evil-snipe




(use-package evil-snipe

:after evil
:demand
:config
(evil-snipe-mode +1)
(evil-snipe-override-mode +1))





5.2.6. evil-nerd-commenter




(use-package evil-nerd-commenter

:general
(general-nvmap
"gc" 'evilnc-comment-operator
"gC" 'evilnc-copy-and-comment-operator)
)





5.2.7. evil-surround




(



  • Use S) to surround something without spaces e.g. (sexp)

  • Use S( to surround something with spaces e.g. ( sexp )



)


(use-package evil-surround

:general
(:states 'operator
"s" 'evil-surround-edit
"S" 'evil-Surround-edit)
(:states 'visual
"S" 'evil-surround-region
"gS" 'evil-Surround-region))





5.2.8. evil-indent-plus




To select a function in python:



  • Stand on a line in the body of the function (root, not an if)

  • Select with vik


(use-package evil-indent-plus

:after evil
:demand
:config
(define-key evil-inner-text-objects-map "i" 'evil-indent-plus-i-indent)
(define-key evil-outer-text-objects-map "i" 'evil-indent-plus-a-indent)
(define-key evil-inner-text-objects-map "k" 'evil-indent-plus-i-indent-up)
(define-key evil-outer-text-objects-map "k" 'evil-indent-plus-a-indent-up)
(define-key evil-inner-text-objects-map "j" 'evil-indent-plus-i-indent-up-down)
(define-key evil-outer-text-objects-map "j" 'evil-indent-plus-a-indent-up-down)
)





5.2.9. evil cleverparens: outer form text object




NOTE:



  • Mark the outer form with v a f


(use-package evil-cleverparens

:after evil
:hook (emacs-lisp-mode . lc/init-cleverparens)
:init
(defun lc/init-cleverparens ()
(require 'evil-cleverparens-util)
(evil-define-text-object evil-cp-a-defun (count &optional beg end type)
"An outer text object for a top level sexp (defun)."
(if (evil-cp--inside-form-p)
(let ((bounds (evil-cp--top-level-bounds)))
(evil-range (car bounds) (cdr bounds) 'inclusive :expanded t))
(error "Not inside a sexp.")))

(evil-define-text-object evil-cp-inner-defun (count &optional beg end type)
"An inner text object for a top level sexp (defun)."
(if (evil-cp--inside-form-p)
(let ((bounds (evil-cp--top-level-bounds)))
(evil-range (1+ (car bounds)) (1- (cdr bounds)) 'inclusive :expanded t))
(error "Not inside a sexp.")))

(define-key evil-outer-text-objects-map "f" #'evil-cp-a-defun)
(define-key evil-inner-text-objects-map "f" #'evil-cp-inner-defun)
)
)






5.2.10. evil-iedit-state




Keybindings:



TAB

toggle occurrence


n / N

next/prev occurrence

F

restrict scope to function


J / K

extend scope of match down/up

V

toggle visibility of matches


(use-package evil-iedit-state

:straight (evil-iedit-state :type git :host github :repo "kassick/evil-iedit-state" :branch "master")
:general
(lc/leader-keys
"s e" '(evil-iedit-state/iedit-mode :wk "iedit")
"s q" '(evil-iedit-state/quit-iedit-mode :wk "iedit quit")))





5.2.11. evil-mc: multi cursor




(use-package evil-mc

:after evil
:demand
:general
(general-nmap
"M-n" #'evil-mc-make-and-goto-next-match
)
(general-vmap
;; "gm" '(:keymap evil-mc-cursors-map)
"A" #'evil-mc-make-cursor-in-visual-selection-end
"I" #'evil-mc-make-cursor-in-visual-selection-beg)
(general-nmap
"gm" '(:keymap evil-mc-cursors-map)
"Q" #'evil-mc-undo-all-cursors
;; "M-p" #'evil-mc-make-and-goto-prev-cursor
)
:config
(global-evil-mc-mode 1)
)





5.2.12. Fix scroll error when centaur tabs is active




Taken from this PR: https://github.com/emacs-evil/evil/pull/1323/files


(use-package evil

:init
(defun lc/evil-posn-x-y (position)
(let ((xy (posn-x-y position)))
(when header-line-format
(setcdr xy (+ (cdr xy)
(or (and (fboundp 'window-header-line-height)
(window-header-line-height))
evil-cached-header-line-height
(setq evil-cached-header-line-height (evil-header-line-height))))))
(when (fboundp 'window-tab-line-height)
(setcdr xy (+ (cdr xy) (window-tab-line-height))))
xy))
:config
(advice-add 'evil-posn-x-y :override #'lc/evil-posn-x-y)
)






5.3. which-key




(use-package which-key

:demand
:general
(lc/leader-keys
"?" 'which-key-show-top-level
)
:init
(setq which-key-separator " ")
(setq which-key-prefix-prefix "+")
;; (setq which-key-idle-delay 0.5)
:config
(which-key-mode))






6. Org






6.1. org mode




Interesting bits:



  • If you use + in lists it will show up as below:

    • subitem



  • you can cycle to next TODO state with org-shiftright

  • You can access custom agenda views with org-agenda, mapped to SPC o A

  • Yo insert a src block use , i and then type initials e.g. jp for jupyter-python


(use-package org

;; :straight org-plus-contrib
;; :straight (:type built-in)
:hook ((org-mode . prettify-symbols-mode)
(org-mode . visual-line-mode)
(org-mode . variable-pitch-mode))
:general
(lc/leader-keys
"f t" '(org-babel-tangle :wk "tangle")
"o C" '(org-capture :wk "capture")
"o l" '(org-todo-list :wk "todo list")
"o c" '((lambda () (interactive)
(persp-switch "main")
(find-file (concat user-emacs-directory "readme.org")))
:wk "open config")
)
(lc/local-leader-keys
:keymaps 'org-mode-map
"a" '(org-archive-subtree :wk "archive subtree")
"E" '(org-export-dispatch :wk "export")
"i" '(org-insert-structure-template :wk "insert src")
"l" '(:ignore true :wk "link")
"l l" '(org-insert-link :wk "insert link")
"l s" '(org-store-link :wk "store link")
"L" '((lambda () (interactive) (org-latex-preview)) :wk "latex preview")
;; "L" '((lambda () (interactive) (org--latex-preview-region (point-min) (point-max))) :wk "latex")
"r" '(org-refile :wk "refile")
"n" '(org-toggle-narrow-to-subtree :wk "narrow subtree")
"p" '(org-priority :wk "priority")
"q" '(org-set-tags-command :wk "tag")
"s" '(org-sort :wk "sort")
"t" '(:ignore true :wk "todo")
"t t" '(org-todo :wk "heading todo")
"t s" '(org-schedule :wk "schedule")
"t d" '(org-deadline :wk "deadline")
"x" '(org-toggle-checkbox :wk "toggle checkbox")
)
(org-mode-map
:states 'insert
"TAB" 'nil
"S-TAB" nil)
(org-mode-map
:states 'normal
"z i" '(org-toggle-inline-images :wk "inline images"))
:init
;; general settings
(when (file-directory-p "~/org")
(setq org-directory "~/org"
+org-export-directory "~/org/export"
org-default-notes-file "~/org/personal/todo.org"
org-id-locations-file "~/org/.orgids"
))
(setq ;; org-export-in-background t
org-src-preserve-indentation t ;; do not put two spaces on the left
org-startup-indented t
;; org-startup-with-inline-images t
org-hide-emphasis-markers t
org-catch-invisible-edits 'smart)
(setq org-image-actual-width nil)
(setq org-indent-indentation-per-level 1)
(setq org-list-demote-modify-bullet '(("-" . "+") ("+" . "*")))
;; disable modules for faster startup
(setq org-modules
'(ol-docview
org-habit))
(setq org-todo-keywords
'((sequence "TODO(t)" "NEXT(n)" "PROG(p)" "|" "HOLD(h)" "DONE(d)")))
(setq-default prettify-symbols-alist '(("#+BEGIN_SRC" . "»")
("#+END_SRC" . "«")
("#+begin_src" . "»")
("#+end_src" . "«")
("lambda" . "λ")
("->" . "→")
("->>" . "↠")))
(setq prettify-symbols-unprettify-at-point 'right-edge)
:config
;; (efs/org-font-setup)
(add-to-list 'org-structure-template-alist '("sh" . "src shell"))
(add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp"))
(add-to-list 'org-structure-template-alist '("py" . "src python"))
(add-to-list 'org-structure-template-alist '("clj" . "src clojure"))
(add-to-list 'org-structure-template-alist '("jp" . "src jupyter-python"))
(add-to-list 'org-structure-template-alist '("jr" . "src jupyter-R"))
;; fontification
(add-to-list 'org-src-lang-modes '("jupyter-python" . python))
(add-to-list 'org-src-lang-modes '("jupyter-R" . R))
;; latex
;; (setq org-latex-compiler "xelatex")
;; see https://www.reddit.com/r/emacs/comments/l45528/questions_about_mving_from_standard_latex_to_org/gkp4f96/?utm_source=reddit&utm_medium=web2x&context=3
;; (setq org-latex-pdf-process '("TEXINPUTS=:$HOME/git/AltaCV//: tectonic %f"))
(setq org-latex-pdf-process '("tectonic %f"))
(add-to-list 'org-export-backends 'beamer)
(plist-put org-format-latex-options :scale 1.2)
)





6.2. org code blocks in monospace font




(use-package org

:config
(defun my-adjoin-to-list-or-symbol (element list-or-symbol)
(let ((list (if (not (listp list-or-symbol))
(list list-or-symbol)
list-or-symbol)))
(require 'cl-lib)
(cl-adjoin element list)))

(eval-after-load "org"
'(mapc
(lambda (face)
(set-face-attribute
face nil
:inherit
(my-adjoin-to-list-or-symbol
'fixed-pitch
(face-attribute face :inherit))))
(list 'org-code 'org-block
;; 'org-table 'org-block-background
)))
)






6.3. org agenda




(use-package org

:general
(lc/leader-keys
"o a" '(org-agenda-list :wk "agenda")
"o A" '(org-agenda :wk "agenda")
"o C" '(org-capture :wk "capture")
"o l" '(org-todo-list :wk "todo list")
"o c" '((lambda () (interactive)
(find-file (concat user-emacs-directory "readme.org")))
:wk "open config")
"o n" '((lambda () (interactive) (org-agenda nil "n")) :wk "next")
"o t" '((lambda () (interactive)
(find-file (concat org-directory "/personal/todo.org")))
:wk "open todos"))
:init
(setq org-agenda-files '())
;; if org folder exists, use agenda files
(when (file-directory-p "~/org/personal")
(setq org-agenda-files
(append org-agenda-files
'("~/org/personal/birthdays.org"))))

;; if roam work folder exists, add to agenda files
(when (file-directory-p "~/roam/work")
(setq org-agenda-files
(append org-agenda-files
'("~/roam/work/todo.org"))))

(setq org-agenda-custom-commands
'(("d" "Dashboard"
((agenda "" ((org-deadline-warning-days 7)))
(todo "NEXT"
((org-agenda-overriding-header "Next Tasks")))
(tags-todo "agenda/ACTIVE" ((org-agenda-overriding-header "Active Projects")))))
("n" "Next Tasks"
((todo "NEXT"
((org-agenda-overriding-header "Next Tasks")))))
("w" "Work Tasks" tags-todo "+work")))
)





6.4. org capture templates




(use-package org

:init
(setq org-capture-templates
`(("b" "Blog" entry
(file+headline "personal/todo.org" "Blog")
,(concat "* WRITE %^{Title} %^g\n"
"SCHEDULED: %^t\n"
":PROPERTIES:\n"
":CAPTURED: %U\n:END:\n\n"
"%i%?"))
("d" "New Diary Entry" entry(file+olp+datetree"~/org/personal/diary.org" "Daily Logs")
"* %^{thought for the day}
:PROPERTIES:
:CATEGORY: %^{category}
:SUBJECT: %^{subject}
:MOOD: %^{mood}
:END:
:RESOURCES:
:END:

\*What was one good thing you learned today?*:
- %^{whatilearnedtoday}

\*List one thing you could have done better*:
- %^{onethingdobetter}

\*Describe in your own words how your day was*:
- %?")
("i" "Inbox" entry
(file+headline "personal/todo.org" "Inbox")
,(concat "* %^{Title}\n"
":PROPERTIES:\n"
":CAPTURED: %U\n"
":END:\n\n"
"%i%l"))
("u" "New URL Entry" entry
(file+function "~/org/personal/dailies.org" org-reverse-datetree-goto-date-in-file)
"* [[%^{URL}][%^{Description}]] %^g %?")
("w" "Work" entry
(file+headline "personal/todo.org" "Work")
,(concat "* TODO [#A] %^{Title} :@work:\n"
"SCHEDULED: %^t\n"
":PROPERTIES:\n:CAPTURED: %U\n:END:\n\n"
"%i%?"))))
)






6.5. cycle only one heading




(use-package org

:init
(defun +org-cycle-only-current-subtree-h (&optional arg)
"Toggle the local fold at the point, and no deeper.
`org-cycle's standard behavior is to cycle between three levels: collapsed,
subtree and whole document. This is slow, especially in larger org buffer. Most
of the time I just want to peek into the current subtree -- at most, expand
*only* the current subtree.

All my (performant) foldings needs are met between this and `org-show-subtree'
(on zO for evil users), and `org-cycle' on shift-TAB if I need it."
(interactive "P")
(unless (eq this-command 'org-shifttab)
(save-excursion
(org-beginning-of-line)
(let (invisible-p)
(when (and (org-at-heading-p)
(or org-cycle-open-archived-trees
(not (member org-archive-tag (org-get-tags))))
(or (not arg)
(setq invisible-p (outline-invisible-p (line-end-position)))))
(unless invisible-p
(setq org-cycle-subtree-status 'subtree))
(org-cycle-internal-local)
t)))))
:config
;; Only fold the current tree, rather than recursively
(add-hook 'org-tab-first-hook #'+org-cycle-only-current-subtree-h)
)






6.6. async tangle




Taken from https://github.com/KaratasFurkan/.emacs.d


(use-package org

:config
(require 's)
(defun lc/async-process (command &optional name filter)
"Start an async process by running the COMMAND string with bash. Return the
process object for it.

NAME is name for the process. Default is \"async-process\".

FILTER is function that runs after the process is finished, its args should be
\"(process output)\". Default is just messages the output."
(make-process
:command `("bash" "-c" ,command)
:name (if name name
"async-process")
:filter (if filter filter
(lambda (process output) (message (s-trim output))))))


(defun lc/tangle-config ()
"Export code blocks from the literate config file
asynchronously."
(interactive)
(let ((command (if (file-directory-p "/Applications/Emacs.app")
"/Applications/Emacs.app/Contents/MacOS/Emacs %s --batch --eval '(org-babel-tangle nil \"%s\")'"
;; on iPad
"emacs %s --batch --eval '(org-babel-tangle nil \"%s\")' 2>&1 | grep -v '^Loading.*\.\.\.$' | grep -v '^Using ' | grep -v '^dump '| grep -v '^Finding '"
)))
;; prevent emacs from killing until tangle-process finished
(add-to-list 'kill-emacs-query-functions
(lambda ()
(or (not (process-live-p (get-process "tangle-process")))
(y-or-n-p "\"fk/tangle-config\" is running; kill it? "))))
;; tangle config asynchronously
(lc/async-process
(format command
(expand-file-name "readme.org" user-emacs-directory)
(expand-file-name "init.el" user-emacs-directory))
"tangle-process")
)

)
)






6.7. org reverse datetree




(use-package org-reverse-datetree

:after org :demand)





6.8. org-superstar




(use-package org-superstar

:hook (org-mode . org-superstar-mode)
:init
(setq org-superstar-headline-bullets-list '("✖" "✚" "◉" "○" "▶")
;; org-superstar-special-todo-items t
org-ellipsis " ↴ ")
)





6.9. highlight todo




Look at hl-todo-keyword-faces to know the keywords (can't get to change them..).



(use-package hl-todo

:hook ((prog-mode org-mode) . lc/hl-todo-init)
:init
(defun lc/hl-todo-init ()
(setq-local hl-todo-keyword-faces '(("HOLD" . "#cfdf30")
("TODO" . "#ff9977")
("NEXT" . "#b6a0ff")
("PROG" . "#00d3d0")
("FIXME" . "#ff9977")
("DONE" . "#44bc44")
("REVIEW" . "#6ae4b9")
("DEPRECATED" . "#bfd9ff")))
(hl-todo-mode))
)





6.10. org babel




(use-package org

:general
(lc/local-leader-keys
:keymaps 'org-mode-map
"'" '(org-edit-special :wk "edit")
"-" '(org-babel-demarcate-block :wk "split block")
"z" '(org-babel-hide-result-toggle :wk "fold result"))
(lc/local-leader-keys
:keymaps 'org-src-mode-map
"'" '(org-edit-src-exit :wk "exit")) ;;FIXME
:init
(setq org-confirm-babel-evaluate nil)
:config
(org-babel-do-load-languages
'org-babel-load-languages
'((emacs-lisp . t)
;; (ledger . t)
(shell . t)))
(add-hook 'org-babel-after-execute-hook 'org-display-inline-images 'append)
)

;; enable mermaid diagram blocks
;; (use-package ob-mermaid
;; :custom (ob-mermaid-cli-path "~/.asdf/shims/mmdc"))






6.11. ob-async




(use-package ob-async

:hook (org-load . (lambda () (require 'ob-async)))
:init
(setq ob-async-no-async-languages-alist '("jupyter-python" "jupyter-R" "jupyter-julia")))





6.12. org-tree-slide




(use-package org-tree-slide

:after org
:hook ((org-tree-slide-play . (lambda () (+remap-faces-at-start-present)))
(org-tree-slide-stop . (lambda () (+remap-faces-at-stop-present))))
:general
(lc/leader-keys
"t p" '(org-tree-slide-mode :wk "present"))
(general-nmap
:keymaps '(org-tree-slide-mode-map org-mode-map)
"C-j" 'org-tree-slide-move-next-tree
"C-k" 'org-tree-slide-move-previous-tree)
:init
(setq org-tree-slide-activate-message "Presentation mode ON")
(setq org-tree-slide-deactivate-message "Presentation mode OFF")
(setq org-tree-slide-indicator nil)
(setq org-tree-slide-breadcrumbs " > ")
(setq org-tree-slide-heading-emphasis t)
(setq org-tree-slide-slide-in-waiting 0.025)
(setq org-tree-slide-content-margin-top 4)
(defun +remap-faces-at-start-present ()
(setq-local face-remapping-alist '((default (:height 1.50) variable-pitch)
(fixed-pitch (:height 1.2) fixed-pitch)
;; (org-verbatim (:height 1.2) org-verbatim)
;; (org-block (:height 1.2) org-block)
))
;; (setq-local olivetti-body-width 95)
(olivetti-mode 1)
(display-fill-column-indicator-mode 0)
(hide-mode-line-mode 1)
(diff-hl-mode 0)
(centaur-tabs-mode 0))
(defun +remap-faces-at-stop-present ()
(setq-local face-remapping-alist '((default variable-pitch default)))
;; (setq-local olivetti-body-width 120)
(olivetti-mode 0)
(display-fill-column-indicator-mode 1)
(hide-mode-line-mode 0)
(doom-modeline-mode 1)
(diff-hl-mode 1)
(centaur-tabs-mode 1))
(setq org-tree-slide-breadcrumbs nil)
(setq org-tree-slide-header nil)
(setq org-tree-slide-slide-in-effect nil)
(setq org-tree-slide-heading-emphasis nil)
(setq org-tree-slide-cursor-init t)
(setq org-tree-slide-modeline-display nil)
(setq org-tree-slide-skip-done nil)
(setq org-tree-slide-skip-comments t)
(setq org-tree-slide-fold-subtrees-skipped t)
(setq org-tree-slide-skip-outline-level 8) ;; or 0?
(setq org-tree-slide-never-touch-face t)
;; :config
;; (org-tree-slide-presentation-profile)
;; :custom-face
;; (org-tree-slide-heading-level-1 ((t (:height 1.8 :weight bold))))
;; (org-tree-slide-heading-level-2 ((t (:height 1.5 :weight bold))))
;; (org-tree-slide-heading-level-3 ((t (:height 1.5 :weight bold))))
;; (org-tree-slide-heading-level-4 ((t (:height 1.5 :weight bold))))
)





6.13. evil-org-mode




Taken from DOOM:



  • nice +org/insert-item-below function


  • evil bindings for org-agenda

  • text objects:

    • use vie to select everything inside a src block

    • use vir to select everything inside a heading

    • use =ie to format a code block




(use-package evil-org-mode

:straight (evil-org-mode :type git :host github :repo "hlissner/evil-org-mode")
:hook ((org-mode . evil-org-mode)
(org-mode . (lambda ()
(require 'evil-org)
(evil-normalize-keymaps)
(evil-org-set-key-theme '(textobjects))
(require 'evil-org-agenda)
(evil-org-agenda-set-keys))))
:bind
([remap evil-org-org-insert-heading-respect-content-below] . +org/insert-item-below) ;; "<C-return>"
([remap evil-org-org-insert-todo-heading-respect-content-below] . +org/insert-item-above) ;; "<C-S-return>"
:general
(general-nmap
:keymaps 'org-mode-map
:states 'normal
"RET" #'org-open-at-point
;; "RET" #'+org/dwim-at-point
)
:init
(defun +org--insert-item (direction)
(let ((context (org-element-lineage
(org-element-context)
'(table table-row headline inlinetask item plain-list)
t)))
(pcase (org-element-type context)
;; Add a new list item (carrying over checkboxes if necessary)
((or `item `plain-list)
;; Position determines where org-insert-todo-heading and org-insert-item
;; insert the new list item.
(if (eq direction 'above)
(org-beginning-of-item)
(org-end-of-item)
(backward-char))
(org-insert-item (org-element-property :checkbox context))
;; Handle edge case where current item is empty and bottom of list is
;; flush against a new heading.
(when (and (eq direction 'below)
(eq (org-element-property :contents-begin context)
(org-element-property :contents-end context)))
(org-end-of-item)
(org-end-of-line)))

;; Add a new table row
((or `table `table-row)
(pcase direction
('below (save-excursion (org-table-insert-row t))
(org-table-next-row))
('above (save-excursion (org-shiftmetadown))
(+org/table-previous-row))))

;; Otherwise, add a new heading, carrying over any todo state, if
;; necessary.
(_
(let ((level (or (org-current-level) 1)))
;; I intentionally avoid `org-insert-heading' and the like because they
;; impose unpredictable whitespace rules depending on the cursor
;; position. It's simpler to express this command's responsibility at a
;; lower level than work around all the quirks in org's API.
(pcase direction
(`below
(let (org-insert-heading-respect-content)
(goto-char (line-end-position))
(org-end-of-subtree)
(insert "\n" (make-string level ?*) " ")))
(`above
(org-back-to-heading)
(insert (make-string level ?*) " ")
(save-excursion (insert "\n"))))
(when-let* ((todo-keyword (org-element-property :todo-keyword context))
(todo-type (org-element-property :todo-type context)))
(org-todo
(cond ((eq todo-type 'done)
;; Doesn't make sense to create more "DONE" headings
(car (+org-get-todo-keywords-for todo-keyword)))
(todo-keyword)
('todo)))))))

(when (org-invisible-p)
(org-show-hidden-entry))
(when (and (bound-and-true-p evil-local-mode)
(not (evil-emacs-state-p)))
(evil-insert 1))))

(defun +org/insert-item-below (count)
"Inserts a new heading, table cell or item below the current one."
(interactive "p")
(dotimes (_ count) (+org--insert-item 'below)))

(defun +org/insert-item-above (count)
"Inserts a new heading, table cell or item above the current one."
(interactive "p")
(dotimes (_ count) (+org--insert-item 'above)))

)






6.14. exporters   org_export






6.14.1. org-html-themify




I use this package to export my config to HTML. I then push it to the gh-pages branch



(use-package org-html-themify

:after modus-themes
:straight
(org-html-themify
:type git
:host github
:repo "DogLooksGood/org-html-themify"
:files ("*.el" "*.js" "*.css"))
:hook (org-mode . org-html-themify-mode)
:init
(setq org-html-themify-themes
'((light . modus-operandi)
(dark . modus-operandi)))
:config
;; otherwise it complains about invalid face
(require 'hl-line)

)

(use-package htmlize
:after org-html-themify)






6.14.2. ox-gfm




(use-package ox-gfm

:commands (org-gfm-export-as-markdown org-gfm-export-to-markdown)
:after org
)





6.14.3. ox-ipynb




The first block of the org file is used to determine the exported language.
To override this we can write:



#+OX-IPYNB-LANGUAGE: ipython


It seems that also jupyter-python should be replaced with ipython for the export to work.


(use-package ox-ipynb

:straight (ox-ipynb :type git :host github :repo "jkitchin/ox-ipynb")
:commands (ox-ipynb-export-org-file-to-ipynb-file))





6.14.4. ox-reveal




(use-package org-re-reveal

:after org
:init
;; (setq org-re-reveal-root (expand-file-name "../../" (locate-library "dist/reveal.js" t))
;; org-re-reveal-revealjs-version "4")
(setq org-re-reveal-root "./reveal.js"
org-re-reveal-revealjs-version "3.8"
org-re-reveal-external-plugins '((progress . "{ src: '%s/plugin/toc-progress/toc-progress.js', async: true, callback: function() { toc_progress.initialize(); toc_progress.create();} }"))
))





6.14.5. weblorg




(use-package weblorg)

(use-package templatel)

(use-package htmlize)






6.14.6. ox-cv




(use-package ox-altacv

:straight (ox-altacv :type git :host github :repo "lccambiaghi/org-cv")
:config (require 'ox-altacv))






6.15. org-appear




Automatically disaply emphasis markers and links when the cursor is on them.



(use-package org-appear

:straight (org-appear :type git :host github :repo "awth13/org-appear")
:hook (org-mode . org-appear-mode)
:init
(setq org-appear-autoemphasis t)
(setq org-appear-autolinks t)
(setq org-appear-autosubmarkers t)
)






6.16. automatic latex preview




(use-package org-fragtog

:hook (org-mode . org-fragtog-mode))





6.17. org-encrypt




  • Use org-encrypt-entry on a headline

  • Use org-decrypt-entry to decrypt



(use-package org

:config
(require 'org-crypt)
(require 'epa-file)
(epa-file-enable)
(org-crypt-use-before-save-magic)
(setq org-tags-exclude-from-inheritance (quote ("crypt")))
(setq org-crypt-key nil)
(defun ag/reveal-and-move-back ()
(org-reveal)
(goto-char ag/old-point))
(defun ag/org-reveal-after-save-on ()
(setq ag/old-point (point))
(add-hook 'after-save-hook 'ag/reveal-and-move-back))
(defun ag/org-reveal-after-save-off ()
(remove-hook 'after-save-hook 'ag/reveal-and-move-back))
(add-hook 'org-babel-pre-tangle-hook 'ag/org-reveal-after-save-on)
(add-hook 'org-babel-post-tangle-hook 'ag/org-reveal-after-save-off)
)





6.18. use org-id in links




Taken from https://writequit.org/articles/emacs-org-mode-generate-ids.html


Problem: when exporting org files to HTML, the header anchors are volatile.
Once I publish a new HTML version of this file, the previous version's links are no longer valid.


This function adds CUSTOM_ID property to all headings in a file (one-time).
We can then use this to link to that heading forever.


Adding it as a after-save-hook automatically adds a CUSTOM_ID to newly created headers.


(use-package org

:init
(defun lc/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 `org-id-new'. In any
case, the CUSTOM_ID of the entry is returned."
(interactive)
(org-with-point-at pom
(let ((id (org-entry-get nil "CUSTOM_ID")))
(cond
((and id (stringp id) (string-match "\\S-" id))
id)
(create
(setq id (org-id-new (concat prefix "h")))
(org-entry-put pom "CUSTOM_ID" id)
(org-id-add-location id (buffer-file-name (buffer-base-buffer)))
id)))))

(defun lc/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" 10000 t)
(org-map-entries (lambda () (lc/org-custom-id-get (point) 'create))))))
:config
(require 'org-id)
(setq org-id-link-to-org-use-id 'create-if-interactive-and-no-custom-id)
)





6.19. org-jupyter-mode   prog_jupyter




(use-package emacs

:hook
((org-jupyter-mode . (lambda () (visual-line-mode -1)
(advice-add 'org-cycle :around #'lc/org-cycle-or-py-complete)))
(org-mode . (lambda () (when (lc/is-jupyter-org-buffer?) (org-jupyter-mode)))))
:init
(defun lc/is-jupyter-org-buffer? ()
(with-current-buffer (buffer-name)
(goto-char (point-min))
(re-search-forward "begin_src jupyter-" 10000 t)))
(defun lc/org-cycle-or-py-complete (orig-fun &rest args)
"If in a jupyter-python code block, call py-indent-or-complete, otherwise use org-cycle"
(if (and (org-in-src-block-p)
(eq (intern (org-element-property :language (org-element-at-point))) 'jupyter-python))
(lc/py-indent-or-complete)
(apply orig-fun args)))
(define-minor-mode org-jupyter-mode
"Minor mode which is active when an org file has the string begin_src jupyter-python
in the first few hundred rows"
;; :keymap (let ((map (make-sparse-keymap)))
;; (define-key map (kbd "C-c f") 'insert-foo)
;; map)
)
)





6.20. org-roam   org_roam




To set up roam:



  • ln -s ~/OneDrive/.../worknotes ~/roam/work

  • ln -s /Users/cambiaghiluca/Library/Mobile\ Documents/iCloud\~com\~appsonthemove\~beorg/Documents/org/roam ~/roam/personal


:CUSTOMID: h:7a406451-6ae8-4d26-a80b-3b7d9e29c023



(use-package org-roam

:after org
:init
(setq org-roam-directory (file-truename "~/roam"))
(setq org-roam-v2-ack t)
(setq org-roam-capture-templates
'(("d" "default" plain "%?" :target
(file+head "personal/%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n") :unnarrowed t)
("w" "work" plain "%?" :target
(file+head "work/%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n") :unnarrowed t)))
:general
(lc/leader-keys
"TAB n" '((lambda () (interactive) (persp-switch "notes")) :wk "notes")
"n b" 'org-roam-buffer-toggle
"n f" 'org-roam-node-find
"n g" 'org-roam-graph
"n i" 'org-roam-node-insert
"n c" 'org-roam-capture
"n t" 'org-roam-tag-add
"n r" 'org-roam-ref-add
"n a" 'org-roam-alias-add
;; Dailies
"n j" 'org-roam-dailies-capture-today
"n J" 'org-roam-dailies-goto-today
;; todos
"o t" '((lambda () (interactive)
(persp-switch "notes")
(find-file (concat org-roam-directory "/work/todo.org")))
:wk "work todos")
"o n" '((lambda () (interactive)
(persp-switch "notes")
(org-roam-node-find))
:wk "notes")
)
:config
(org-roam-setup)
;; If using org-roam-protocol
;; (require 'org-roam-protocol)
(add-to-list 'display-buffer-alist
'(("*org-roam*"
(display-buffer-in-direction)
(direction . right)
(window-width . 0.33)
(window-height . fit-window-to-buffer))))

)






7. UI






7.1. all the icons




(use-package all-the-icons

:if (not lc/is-ipad)
:demand
)





7.2. all the icons completion




(use-package all-the-icons-completion

:after (marginalia all-the-icons)
:hook (marginalia-mode . all-the-icons-completion-marginalia-setup)
:init
(all-the-icons-completion-mode))





7.3. doom modeline




  (use-package doom-modeline

:demand
:init
(setq doom-modeline-buffer-encoding nil)
(setq doom-modeline-env-enable-python nil)
(setq doom-modeline-height 15)
(setq doom-modeline-project-detection 'projectile)
:config
(doom-modeline-mode 1)
(set-face-attribute 'doom-modeline-evil-insert-state nil :foreground "orange")
)





7.4. Modus themes + alternate light/dark themes   modus_themes




I use a particular emacs build which has additional feature for macOS: https://github.com/railwaycat/homebrew-emacsmacport
This defines a hook that is run when macOS system theme changes from light to dark.
We use this hook to switch from light to dark theme.


(use-package modus-themes

:straight (modus-themes :type git :host gitlab :repo "protesilaos/modus-themes" :branch "main")
:demand
:if (display-graphic-p)
:hook (modus-themes-after-load-theme . lc/fix-fill-column-indicator)
:general
(lc/leader-keys
"t t" '((lambda () (interactive) (modus-themes-toggle)) :wk "toggle theme"))
:init
(setq modus-themes-slanted-constructs t
;; modus-themes-no-mixed-fonts t
modus-themes-bold-constructs t
modus-themes-fringes 'nil ; {nil,'subtle,'intense}
modus-themes-mode-line '3d ; {nil,'3d,'moody}
modus-themes-intense-hl-line nil
modus-themes-mixed-fonts t
modus-themes-prompts nil ; {nil,'subtle,'intense}
modus-themes-completions 'moderate ; {nil,'moderate,'opinionated}
modus-themes-diffs nil ; {nil,'desaturated,'fg-only}
modus-themes-org-blocks 'greyscale ; {nil,'greyscale,'rainbow}
modus-themes-headings ; Read further below in the manual for this one
'((1 . line-no-bold)
(t . rainbow-line-no-bold))
modus-themes-variable-pitch-headings t
modus-themes-scale-headings t
modus-themes-scale-1 1.1
modus-themes-scale-2 1.15
modus-themes-scale-3 1.21
modus-themes-scale-4 1.27
modus-themes-scale-5 1.33)
(defun lc/override-colors ()
(setq modus-themes-operandi-color-overrides
'((bg-main . "#fefcf4")
(bg-dim . "#faf6ef")
(bg-alt . "#f7efe5")
(bg-hl-line . "#f4f0e3")
(bg-active . "#e8dfd1")
(bg-inactive . "#f6ece5")
(bg-region . "#c6bab1")
(bg-header . "#ede3e0")
(bg-tab-bar . "#dcd3d3")
(bg-tab-active . "#fdf6eb")
(bg-tab-inactive . "#c8bab8")
(fg-unfocused ."#55556f")))
(setq modus-themes-vivendi-color-overrides
'((bg-main . "#100b17")
(bg-dim . "#161129")
(bg-alt . "#181732")
(bg-hl-line . "#191628")
(bg-active . "#282e46")
(bg-inactive . "#1a1e39")
(bg-region . "#393a53")
(bg-header . "#202037")
(bg-tab-bar . "#262b41")
(bg-tab-active . "#120f18")
(bg-tab-inactive . "#3a3a5a")
(fg-unfocused . "#9a9aab")))
)
(defun lc/load-dark-theme ()
(setq lc/theme 'light)
;; (with-eval-after-load 'org (plist-put org-format-latex-options :foreground "whitesmoke"))
(with-eval-after-load 'org (plist-put org-format-latex-options :background "Transparent"))
(with-eval-after-load 'org-html-themify
(setq org-html-themify-themes '((light . modus-vivendi) (dark . modus-vivendi))))
;; (modus-themes-load-vivendi)
(modus-themes-load-operandi)
(lc/update-centaur-tabs)
)
(defun lc/load-light-theme ()
(setq lc/theme 'light)
;; (with-eval-after-load 'org (plist-put org-format-latex-options :foreground "dark"))
(with-eval-after-load 'org (plist-put org-format-latex-options :background "Transparent"))
(with-eval-after-load 'org-html-themify
(setq org-html-themify-themes '((light . modus-operandi) (dark . modus-operandi))))
(setenv "BAT_THEME" "ansi")
(modus-themes-load-operandi)
(lc/update-centaur-tabs))
(defun lc/update-centaur-tabs ()
(centaur-tabs-display-update)
(centaur-tabs-headline-match)
(set-face-attribute 'centaur-tabs-selected nil :overline (face-background 'centaur-tabs-active-bar-face)))
(defun lc/change-theme-with-mac-system ()
(let ((appearance (plist-get (mac-application-state) :appearance)))
(cond ((equal appearance "NSAppearanceNameAqua")
(lc/load-light-theme))
((equal appearance "NSAppearanceNameDarkAqua")
(lc/load-light-theme)
))))
(defun lc/change-theme-with-timers ()
(run-at-time "00:00" (* 60 60 24) 'lc/load-dark-theme)
(run-at-time "08:00" (* 60 60 24) 'lc/load-light-theme)
(run-at-time "18:00" (* 60 60 24) 'lc/load-dark-theme))
(defun lc/fix-fill-column-indicator ()
(when (display-graphic-p)
(modus-themes-with-colors
(custom-set-faces
`(fill-column-indicator ((,class :background ,bg-inactive :foreground ,bg-inactive)))))))
:config
(when (display-graphic-p)
(lc/override-colors))
(if (and (boundp 'mac-effective-appearance-change-hook)
(plist-get (mac-application-state) :appearance))
(progn
(add-hook 'after-init-hook 'lc/change-theme-with-mac-system)
(add-hook 'mac-effective-appearance-change-hook 'lc/change-theme-with-mac-system))
;; (add-hook 'after-init-hook 'lc/change-theme-with-timers)
;; (add-hook 'emacs-startup-hook 'lc/load-light-theme)
(add-hook 'emacs-startup-hook 'lc/change-theme-with-timers)
)
)





7.5. centaur tabs




(use-package centaur-tabs

:hook (emacs-startup . centaur-tabs-mode)
:general
(general-nmap "gt" 'centaur-tabs-forward
"gT" 'centaur-tabs-backward)
(lc/leader-keys
"b K" '(centaur-tabs-kill-other-buffers-in-current-group :wk "kill other buffers"))
:init
(setq centaur-tabs-set-icons t)
(setq centaur-tabs-set-modified-marker t
centaur-tabs-modified-marker "M"
centaur-tabs-cycle-scope 'tabs)
(setq centaur-tabs-set-close-button nil)
(setq centaur-tabs-enable-ido-completion nil)
:config
(centaur-tabs-mode t)
;; (centaur-tabs-headline-match)
(centaur-tabs-group-by-projectile-project)
)





7.6. dashboard   dashboard




(use-package dashboard

:demand
:init
(setq initial-buffer-choice (lambda () (get-buffer "*dashboard*")))
(setq dashboard-center-content t)
(setq dashboard-projects-backend 'projectile)
(setq dashboard-set-heading-icons t)
(setq dashboard-set-file-icons t)
(defun lc/is-after-17-or-weekends? ()
(or (thread-first (nth 3 (split-string (current-time-string) " ")) ;; time of the day e.g. 18
;; (substring 0 2)
(string-to-number) ;;<
(> 16))
(thread-first (substring (current-time-string) 0 3) ;; day of the week e.g. Fri
(member '("Sat" "Sun")))))
(setq dashboard-banner-logo-title nil)
(setq dashboard-set-footer nil)
;; (setq dashboard-startup-banner [VALUE])
(setq dashboard-set-navigator t)
(setq dashboard-navigator-buttons
`((;; Github
(,(all-the-icons-octicon "mark-github" :height 1.1 :v-adjust 0.0)
"Github"
"Go to wondercast"
(lambda (&rest _) (browse-url "https://github.com/Maersk-Global/wondercast")))
;; Codebase
(,(all-the-icons-faicon "briefcase" :height 1.1 :v-adjust -0.1)
"JIRA"
"Go to Kanban"
(lambda (&rest _) (browse-url "https://jira.maerskdev.net/secure/RapidBoard.jspa?rapidView=6378&projectKey=AVOC&quickFilter=15697")))
;; Perspectives
(,(all-the-icons-octicon "history" :height 1.1 :v-adjust 0.0)
"Restore"
"Restore"
(lambda (&rest _) (persp-state-load persp-state-default-file)))
)))
(defun lc/dashboard-agenda-entry-format ()
"Format agenda entry to show it on dashboard. Compared to the original, we remove tags at the end"
(let* ((schedule-time (org-get-scheduled-time (point)))
(deadline-time (org-get-deadline-time (point)))
(item (org-agenda-format-item
(dashboard-agenda-entry-time (or schedule-time deadline-time))
(org-get-heading)
(org-outline-level)
(org-get-category)
nil;; (org-get-tags)
t))
(loc (point))
(file (buffer-file-name)))
(dashboard-agenda--set-agenda-headline-face item)
(list item loc file)))
(defun lc/dashboard-get-agenda ()
"Get agenda items for today or for a week from now."
(org-compile-prefix-format 'agenda)
(org-map-entries 'lc/dashboard-agenda-entry-format
dashboard-match-agenda-entry
'agenda
dashboard-filter-agenda-entry))
(defun lc/dashboard-get-next ()
"Get agenda items for today or for a week from now."
(org-compile-prefix-format 'agenda)
(org-map-entries 'lc/dashboard-agenda-entry-format
dashboard-match-next-entry
'agenda))
(defun lc/dashboard-insert-next (list-size)
"Add the list of LIST-SIZE items of next tasks"
(require 'org-agenda)
(let ((next (lc/dashboard-get-next)))
(dashboard-insert-section
"Next tasks"
next
list-size
"n"
`(lambda (&rest ignore)
(let ((buffer (find-file-other-window (nth 2 ',el))))
(with-current-buffer buffer
(goto-char (nth 1 ',el))
(switch-to-buffer buffer))))
(format "%s" (nth 0 el)))))
:config
;; exclude work items after 17 and on weekends
(setq dashboard-match-next-entry "TODO=\"NEXT\"-work")
(run-at-time "00:00" (* 60 60 24)
(lambda ()
(if (lc/is-after-17-or-weekends?)
(setq dashboard-match-agenda-entry "life|habits"
dashboard-match-next-entry "TODO=\"NEXT\"-work")
(setq dashboard-match-agenda-entry "work|life|habits"
dashboard-match-next-entry "TODO=\"NEXT\""
))))
(dashboard-setup-startup-hook)
(set-face-attribute 'dashboard-items-face nil :height (lc/get-font-size))
;; do not show tags in agenda view
(advice-add 'dashboard-get-agenda :override #'lc/dashboard-get-agenda)
;; show next tasks in dashboard
(add-to-list 'dashboard-item-generators '(next . lc/dashboard-insert-next))
(setq dashboard-items '((agenda . 5)
(next . 10)
;; (bookmarks . 5)
;; (recents . 5)
(projects . 5)))
)





7.7. popup management




Taken from https://emacs.stackexchange.com/questions/46210/reuse-help-window



(use-package emacs

:init
(setq display-buffer-alist
`((,(rx bos (or "*Apropos*" "*Help*" "*helpful" "*info*" "*Summary*") (0+ not-newline))
(display-buffer-reuse-mode-window display-buffer-below-selected)
(window-height . 0.33)
(mode apropos-mode help-mode helpful-mode Info-mode Man-mode))))
)
;; reuse existing windows
;; (setq display-buffer-alist
;; '((".*"
;; (display-buffer-reuse-window display-buffer-same-window)
;; (reusable-frames . t))))

;; (setq even-window-sizes nil) ; display-buffer hint: avoid resizing






7.8. centered cursor mode   ui_extra




(use-package centered-cursor-mode

:general
(lc/leader-keys
"t =" '((lambda () (interactive) (centered-cursor-mode 'toggle)) :wk "center cursor")
)
)





7.9. hide mode line   ui_extra




  (use-package hide-mode-line

:commands (hide-mode-line-mode))





7.10. winum   ui_extra




(use-package winum

:general
(lc/leader-keys
"1" '(winum-select-window-1 :wk "win 1")
"2" '(winum-select-window-2 :wk "win 2")
"3" '(winum-select-window-3 :wk "win 3")
"4" '(winum-select-window-4 :wk "win 4")
"5" '(winum-select-window-5 :wk "win 5")
"6" '(winum-select-window-6 :wk "win 6")
)
:config
(winum-mode))





7.11. transpose frame   ui_extra




(use-package transpose-frame

:general
(lc/leader-keys
"w t" '(transpose-frame :wk "transpose")
"w f" '(rotate-frame :wk "flip")))





7.12. Fill column indicator




With evil you can:




  • gww to fill the line


  • gqq to fill the line and move to the end of it


  • gwp to fill paragraph


(use-package display-fill-column-indicator

:straight (:type built-in)
:hook
(python-mode . display-fill-column-indicator-mode)
:init
(setq-default fill-column 90)
;; (setq display-fill-column-indicator-character "|")
)





7.13. Highlight indentation guides




;; add a visual intent guide

(use-package highlight-indent-guides
:hook (prog-mode . highlight-indent-guides-mode)
:init
;; (setq highlight-indent-guides-method 'column)
;; (setq highlight-indent-guides-method 'bitmap)
(setq highlight-indent-guides-method 'character)
(setq highlight-indent-guides-character ?‖)
(setq highlight-indent-guides-responsive 'top)
;; (setq highlight-indent-guides-responsive 'stack)
;; (setq highlight-indent-guides-auto-enabled nil)
;; (set-face-background 'highlight-indent-guides-odd-face "darkgray")
;; (set-face-background 'highlight-indent-guides-even-face "dimgray")
;; (set-face-foreground 'highlight-indent-guides-character-face "dimgray")
)





7.14. Enlarge window




Taken from DOOM


(use-package emacs

:general
(lc/leader-keys
"w o" '(doom/window-enlargen :wk "enlargen"))
:init
(defun doom/window-enlargen (&optional arg)
"Enlargen the current window to focus on this one. Does not close other
windows (unlike `doom/window-maximize-buffer'). Activate again to undo."
(interactive "P")
(let ((param 'doom--enlargen-last-wconf))
(cl-destructuring-bind (window . wconf)
(or (frame-parameter nil param)
(cons nil nil))
(set-frame-parameter
nil param
(if (and (equal window (selected-window))
(not arg)
wconf)
(ignore
(let ((source-window (selected-window)))
(set-window-configuration wconf)
(when (window-live-p source-window)
(select-window source-window))))
(prog1 (cons (selected-window) (or wconf (current-window-configuration)))
(let* ((window (selected-window))
(dedicated-p (window-dedicated-p window))
(preserved-p (window-parameter window 'window-preserved-size))
(ignore-window-parameters t)
(window-resize-pixelwise nil)
(frame-resize-pixelwise nil))
(unwind-protect
(progn
(when dedicated-p
(set-window-dedicated-p window nil))
(when preserved-p
(set-window-parameter window 'window-preserved-size nil))
(maximize-window window))
(set-window-dedicated-p window dedicated-p)
(when preserved-p
(set-window-parameter window 'window-preserved-size preserved-p))
(add-hook 'doom-switch-window-hook #'doom--enlargened-forget-last-wconf-h)))))))))
)






7.15. 8 colors theme




When using emacs on my jailbroken iPad, I cannot set TERM=xterm-256color because of some terminfo error.
I therefore do what I can with the 8 colors I can use.


The default theme manoj-dark does a pretty good job OOTB.
I add a few manual tweaks.
The theme defintion gets saved in custom-theme-directory.


(deftheme 8colors

"Theme using only 8 colors")

;; (custom-theme-set-variables
;; '8colors
;; '(overline-margin 0)
;; )

(custom-theme-set-faces
'8colors
'(centaur-tabs-unselected ((t (:foreground "white" :background "black"))) t)
'(centaur-tabs-unselected-modified ((t (:foreground "white" :background "black"))) t)
'(tool-bar ((t (:background "black"))) t)
'(selectrum-current-candidate ((t (:background "blue"))) t)
'(org-code ((t (:foreground "magenta"))) t)
'(org-special-keyword ((t (:foreground "magenta"))) t)
'(mode-line ((t (:background "black"))) t)
'(doom-modeline-buffer-file ((t (:background "black"))) t)
'(tab-line ((t (:background "black"))) t)
'(magit-diff-removed-highlight ((t (:background "red" :foreground "white"))) t)
'(magit-diff-added-highlight ((t (:background "green" :foreground "white"))) t)
'(magit-hash ((t (:background "black" :foreground "white"))) t)
'(iedit-occurrence ((t (:background "blue" :foreground "white"))) t)
)

(provide-theme '8colors)



(use-package emacs

:init
(unless (> (display-color-cells) 8)
(setq custom-theme-directory (concat user-emacs-directory "themes"))
(custom-set-variables '(custom-enabled-themes '(8colors manoj-dark)))
))






8. Completion framework






8.1. marginalia




(use-package marginalia

:after vertico
:init
(setq marginalia-annotators '(marginalia-annotators-heavy marginalia-annotators-light nil))
(marginalia-mode)
(with-eval-after-load 'projectile
(add-to-list 'marginalia-command-categories '(projectile-find-file . file)))
)





8.2. embark




NOTE:



  • You can act on candidates with C-l

  • You can run embark-export on all results (e.g. after a consult-line) with C-l E

    • You can run embark-export-snapshot with C-l S




(use-package embark

:after vertico
:general
(general-nmap "C-l" 'embark-act)
(vertico-map
"C-l" #'embark-act
)
(:keymaps 'embark-file-map
;; "o" 'find-file-other-window
"x" 'lc/dired-open-externally
)
:init
;; Optionally replace the key help with a completing-read interface
(setq prefix-help-command #'embark-prefix-help-command)
:config
;; Hide the mode line of the Embark live/completions buffers
(add-to-list 'display-buffer-alist
'("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*"
nil
(window-parameters (mode-line-format . none))))
;; (add-hook 'embark-setup-hook 'selectrum-set-selected-candidate)
)





8.3. wgrep




After running embark-export, we can edit the results with wgrep and commit the edits.
This is extremely powerful for refactorings such as changing the name of a class or a function across files in the project.


(use-package wgrep

:general
(grep-mode-map "W" 'wgrep-change-to-wgrep-mode)
:init
(setq wgrep-auto-save-buffer t)
(setq wgrep-change-readonly-file t)
)





8.4. consult




NOTE:



  • After consult-line you can press M-n twice to search for the symbol at point


(use-package consult

:commands (consult-ripgrep)
:general
(general-nmap
:states '(normal insert)
"C-p" 'consult-yank-pop)
(lc/leader-keys
"s i" '(consult-isearch :wk "isearch")
"s o" '(consult-outline :which-key "outline")
"s s" 'consult-line
"s p" '(consult-ripgrep :wk "ripgrep project")
"b b" 'consult-buffer
;; TODO consult mark
"f r" 'consult-recent-file
;; "s !" '(consult-flymake :wk "flymake")
)
:init
(setq xref-show-xrefs-function #'consult-xref
xref-show-definitions-function #'consult-xref)
;; (setq consult-preview-key "C-l")
;; (setq consult-narrow-key ">")
:config
(autoload 'projectile-project-root "projectile")
(setq consult-project-root-function #'projectile-project-root)
(with-eval-after-load 'selectrum
(require 'consult-selectrum))
)





8.5. embark-consult




(use-package embark-consult

:after (embark consult)
;; :demand t ; only necessary if you have the hook below
;; if you want to have consult previews as you move around an
;; auto-updating embark collect buffer
;; :hook
;; (embark-collect-mode . embark-consult-preview-minor-mode)
)





8.6. vertico




(use-package vertico

;; :straight (vertico :type git :host github :repo "minad/vertico")
:straight (vertico :files (:defaults "extensions/*")
:includes (vertico-indexed
vertico-flat
vertico-grid
vertico-mouse
;; vertico-quick
vertico-buffer
vertico-repeat
vertico-reverse
vertico-directory
vertico-multiform
vertico-unobtrusive
))
:demand
:hook
((minibuffer-setup . vertico-repeat-save) ; Make sure vertico state is saved for `vertico-repeat'
(rfn-eshadow-update-overlay . vertico-directory-tidy) ; Clean up file path when typing
)
:general
(:keymaps 'vertico-map
"C-j" #'vertico-next
"C-k" #'vertico-previous
"<escape>" #'minibuffer-keyboard-quit ; Close minibuffer
;; "C-;" #'kb/vertico-multiform-flat-toggle
"M-<backspace>" #'vertico-directory-delete-word
)
(:keymaps '(normal insert visual motion)
"M-." #'vertico-repeat) ; Perfectly return to the state of the last Vertico minibuffer usage
;; :bind (:map vertico-map
;; ("C-j" . vertico-next)
;; ("C-k" . vertico-previous)
;; ("<escape>" . minibuffer-keyboard-quit)
;; )
:init
;; (setq vertico-resize t)

;; multiform extension
(setq vertico-grid-separator " ")
(setq vertico-grid-lookahead 50)
(setq vertico-buffer-display-action '(display-buffer-reuse-window))
(setq vertico-multiform-categories
'((file indexed)
(consult-grep buffer)
(consult-location)
(imenu buffer)
(library reverse indexed)
(org-roam-node reverse indexed)
(t reverse)
))
(setq vertico-multiform-commands
'(("flyspell-correct-*" grid reverse)
(org-refile grid reverse indexed)
(consult-yank-pop indexed)
(consult-flycheck)
(consult-lsp-diagnostics)
))
(defun kb/vertico-multiform-flat-toggle ()
"Toggle between flat and reverse."
(interactive)
(vertico-multiform--display-toggle 'vertico-flat-mode)
(if vertico-flat-mode
(vertico-multiform--temporary-mode 'vertico-reverse-mode -1)
(vertico-multiform--temporary-mode 'vertico-reverse-mode 1)))

;; Workaround for problem with `tramp' hostname completions. This overrides
;; the completion style specifically for remote files! See
;; https://github.com/minad/vertico#tramp-hostname-completion
(defun lc/basic-remote-try-completion (string table pred point)
(and (vertico--remote-p string)
(completion-basic-try-completion string table pred point)))
(defun lc/basic-remote-all-completions (string table pred point)
(and (vertico--remote-p string)
(completion-basic-all-completions string table pred point)))
(add-to-list 'completion-styles-alist
'(basic-remote ; Name of `completion-style'
lc/basic-remote-try-completion lc/basic-remote-all-completions nil))

(setq completion-in-region-function
(lambda (&rest args)
(apply (if vertico-mode
#'consult-completion-in-region
#'completion--in-region)
args)))

:config
;; (vertico-multiform-mode)
(vertico-mode)

;; Prefix the current candidate with “» ”. From
;; https://github.com/minad/vertico/wiki#prefix-current-candidate-with-arrow
(advice-add #'vertico--format-candidate :around
(lambda (orig cand prefix suffix index _start)
(setq cand (funcall orig cand prefix suffix index _start))
(concat
(if (= vertico--index index)
(propertize "» " 'face 'vertico-current)
" ")
cand)))


)

(use-package orderless
:init
;; Configure a custom style dispatcher (see the Consult wiki)
;; (setq orderless-style-dispatchers '(+orderless-dispatch)
;; orderless-component-separator #'orderless-escapable-split-on-space)
(setq completion-styles '(orderless)
completion-category-defaults nil
completion-category-overrides '((file (styles partial-completion)))))

(use-package savehist
:init
(savehist-mode))

;; A few more useful configurations...
(use-package emacs
:init
;; Add prompt indicator to `completing-read-multiple'.
;; Alternatively try `consult-completing-read-multiple'.
(defun crm-indicator (args)
(cons (concat "[CRM] " (car args)) (cdr args)))
(advice-add #'completing-read-multiple :filter-args #'crm-indicator)

;; Do not allow the cursor in the minibuffer prompt
(setq minibuffer-prompt-properties
'(read-only t cursor-intangible t face minibuffer-prompt))
(add-hook 'minibuffer-setup-hook #'cursor-intangible-mode)

;; Emacs 28: Hide commands in M-x which do not work in the current mode.
;; Vertico commands are hidden in normal buffers.
;; (setq read-extended-command-predicate
;; #'command-completion-default-include-p)

;; Enable recursive minibuffers
(setq enable-recursive-minibuffers t))






8.7. dabbrev




NOTE:



  • When TAB does not work, use S-TAB as backup


(use-package dabbrev

:general
(python-mode-map
:states 'insert
"<backtab>" 'dabbrev-completion
;; ("C-M-/" . dabbrev-expand)
)
)





8.8. corfu




;; Configure corfu

(use-package corfu
:straight (corfu :type git :host github :repo "minad/corfu")
;; :hook (after-init . corfu-global-mode)
:hook ((prog-mode . corfu-mode)
(org-mode . corfu-mode))
:bind
(:map corfu-map
("C-j" . corfu-next)
("C-k" . corfu-previous))
:general
(evil-insert-state-map "C-k" nil)
:init
(setq corfu-auto nil) ;; Enable auto completion
(setq corfu-cycle t) ;; Enable cycling for `corfu-next/previous'
(setq corfu-min-width 80)
(setq corfu-max-width corfu-min-width) ; Always have the same width
(setq corfu-preselect-first t)

(defun corfu-enable-always-in-minibuffer ()
"Enable Corfu in the minibuffer if Vertico/Mct are not active."
(unless (or (bound-and-true-p mct--active) ; Useful if I ever use MCT
(bound-and-true-p vertico--input))
(setq-local corfu-auto nil) ; Ensure auto completion is disabled
(corfu-mode 1)))
(add-hook 'minibuffer-setup-hook #'corfu-enable-always-in-minibuffer 1)
;; :custom
;; (corfu-commit-predicate nil) ;; Do not commit selected candidates on next input
;; (corfu-quit-at-boundary t) ;; Automatically quit at word boundary
;; (corfu-quit-no-match t) ;; Automatically quit if there is no match
;; (corfu-preview-current nil) ;; Disable current candidate preview
;; (corfu-preselect-first nil) ;; Disable candidate preselection
;; (corfu-echo-documentation nil) ;; Disable documentation in the echo area
;; (corfu-scroll-margin 5) ;; Use scroll margin
)





8.9. kind-icon




(use-package kind-icon

:straight (kind-icon :type git :host github :repo "jdtsmith/kind-icon")
:after corfu :demand
:init
(setq kind-icon-default-face 'corfu-default) ; to compute blended backgrounds correctly
(setq kind-icon-blend-background nil) ; Use midpoint color between foreground and background colors ("blended")?
(setq kind-icon-blend-frac 0.08)
:config
(add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter)
;; refresh kind icon cache to match theme
(with-eval-after-load 'modus-themes
(add-hook 'modus-themes-after-load-theme-hook #'(lambda () (interactive) (kind-icon-reset-cache))))

)







9. Programming






9.1. project






9.1.1. projectile




NOTE:




  • projectile struggles with monorepos where .git folder is at the root but each subproject has e.g a pyproject.toml. In those cases, we need to create a .projectile file in the root of the subprojects.


  • projectile excludes git ignored files from projectile-find-file. Use lc/projectile-find-file-all when opening data


(use-package projectile

:demand
:general
(lc/leader-keys
:states 'normal
"p" '(:keymap projectile-command-map :which-key "project")
"p <escape>" 'keyboard-escape-quit
"p a" '(projectile-add-known-project :wk "add known")
"p F" '(lc/projectile-find-file-all :wk "find file (all)")
"p t" '(projectile-run-vterm :wk "term"))
:init
(when (file-directory-p "~/git")
(setq projectile-project-search-path '("~/git")))
(setq projectile-completion-system 'default)
(setq projectile-project-root-files '(".envrc" ".projectile" "project.clj" "deps.edn"))
(setq projectile-switch-project-action 'projectile-commander)
;; Do not include straight repos (emacs packages) to project list
(setq projectile-ignored-project-function
(lambda (project-root)
(string-prefix-p (expand-file-name "straight/" user-emacs-directory) project-root)))
(defun lc/projectile-find-file-all ()
(interactive)
(let ((projectile-git-command "git ls-files -zco"))
(projectile-find-file)))
:config
(defadvice projectile-project-root (around ignore-remote first activate)
(unless (file-remote-p default-directory) ad-do-it))
(projectile-mode)
;; projectile commander methods
(setq projectile-commander-methods nil)
(def-projectile-commander-method ?? "Commander help buffer."
(ignore-errors (kill-buffer projectile-commander-help-buffer))
(with-current-buffer (get-buffer-create projectile-commander-help-buffer)
(insert "Projectile Commander Methods:\n\n")
(dolist (met projectile-commander-methods)
(insert (format "%c:\t%s\n" (car met) (cadr met))))
(goto-char (point-min))
(help-mode)
(display-buffer (current-buffer) t))
(projectile-commander))
(def-projectile-commander-method ?t
"Open a *shell* buffer for the project."
(projectile-run-vterm))
(def-projectile-commander-method ?\C-? ;; backspace
"Go back to project selection."
(projectile-switch-project))
(def-projectile-commander-method ?d
"Open project root in dired."
(projectile-dired))
(def-projectile-commander-method ?f
"Find file in project."
(projectile-find-file))
(def-projectile-commander-method ?s
"Ripgrep in project."
(consult-ripgrep))
(def-projectile-commander-method ?g
"Git status in project."
(projectile-vc))
)





9.1.2. perspective




NOTE:



  • You can use persp-state-load to restore the perspective when emacs was killed


(use-package perspective

:commands (persp-new persp-switch persp-state-save)
:general
(lc/leader-keys
"TAB" '(:ignore true :wk "tab")
"TAB TAB" 'persp-switch
"TAB `" 'persp-switch-last
"TAB d" 'persp-kill
"TAB h" 'persp-prev
"TAB l" 'persp-next
"TAB x" '((lambda () (interactive) (persp-kill (persp-current-name))) :wk "kill current")
"TAB X" '((lambda () (interactive) (persp-kill (persp-names))) :wk "kill all")
"TAB m" '(lc/main-tab :wk "main"))
:init
(setq persp-state-default-file (expand-file-name ".persp" user-emacs-directory))
(defun lc/main-tab ()
"Jump to the dashboard buffer, if doesn't exists create one."
(interactive)
(persp-switch "main")
(switch-to-buffer dashboard-buffer-name)
(dashboard-mode)
(dashboard-insert-startupify-lists)
(dashboard-refresh-buffer))
(defun lc/is-persp-empty? ()
(seq-filter
;; filter away buffers which should be hidden
(lambda (buffer-name) (not (string-prefix-p "*" buffer-name)))
;; get list of buffer names in current perspective
(mapcar (lambda (elm) (buffer-name (car elm)))
(centaur-tabs-view (centaur-tabs-current-tabset)))
))
:config
(persp-mode)
(add-hook 'kill-emacs-hook #'persp-state-save))





9.1.3. persp-projectile




NOTE:



  • Use SPC TAB r to reload a project when something went wrong with SPC p p


(use-package persp-projectile

:after projectile
:init
(defun lc/get-last-folder-from-known-proj (path)
"/path/to/something/ returns something"
(car (last (split-string path "\/") 2)))
(defun lc/find-project-from-persp (persp-name)
"known-proj returns /path/to/known-proj"
(car
(seq-filter
(lambda (proj) (string= persp-name (lc/get-last-folder-from-known-proj proj)))
projectile-known-projects-on-file)))
(defun lc/persp-reload-project ()
(interactive)
(let* ((persp (persp-current-name))
(proj-root (lc/find-project-from-persp persp)))
(persp-kill persp)
(projectile-persp-switch-project proj-root)))
:general
(lc/leader-keys
"p p" 'projectile-persp-switch-project
"TAB r" '(lc/persp-reload-project :wk "reload")
;; "TAB o" '((lambda () (interactive)
;; (let ((projectile-switch-project-action #'projectile-find-file))
;; (projectile-persp-switch-project "org")))
;; :wk "org")
)
)






9.2. dired and friends




  • Jump to current file with SPC f j

  • With a dired buffer open, use dired-other-window to open another folder where you want to move/copy files from/to

  • Hide details with ( )

  • Show/hide dotfiles with H

  • Mark with m, unmark with u

  • Invert selection with t


  • * has some helpers for marking

  • First mark some files and then K to "hide" them

  • Open directory in right window with S-RET

    • When copying from left window, target will be right window

    • Copy with C



  • Open subdir in buffer below with I

    • Open them as subtree with i



  • Open files with macos with O

  • View files with go and exit with q





9.2.1. dired




(use-package dired

:straight (:type built-in)
:hook
(dired-mode . dired-hide-details-mode)
:general
(lc/leader-keys
"f d" 'dired
"f j" 'dired-jump)
(general-nmap
:keymaps 'dired-mode-map
:states 'normal
"F" '((lambda () (interactive)
(let ((fn (dired-get-file-for-visit)))
(start-process "open-directory" nil "open" "-R" fn)))
:wk "open finder")
"X" '(lc/dired-open-externally :wk "open external"))
:init
(setq dired-omit-files "^\\.[^.]\\|$Rhistory\\|$RData\\|__pycache__")
(setq dired-listing-switches "-lah")
(setq ls-lisp-dirs-first t)
(setq ls-lisp-use-insert-directory-program nil)
(setq dired-dwim-target t)
(defun lc/dired-open-externally ()
"Open marked dired file/folder(s) (or file/folder(s) at point if no marks)
with external application"
(interactive)
(let ((fn (dired-get-file-for-visit)))
(start-process "open-external" nil "open" fn)))
)

(use-package dired-single
:after dired
:general
(dired-mode-map
:states 'normal
"h" 'dired-single-up-directory
"l" 'dired-single-buffer
"q" 'kill-current-buffer))

(use-package all-the-icons-dired
:if (display-graphic-p)
:hook (dired-mode . (lambda () (interactive)
(unless (file-remote-p default-directory)
(all-the-icons-dired-mode)))))

(use-package dired-hide-dotfiles
:hook (dired-mode . dired-hide-dotfiles-mode)
:config
(evil-collection-define-key 'normal 'dired-mode-map
"H" 'dired-hide-dotfiles-mode))






9.2.2. dired subtree




(use-package dired-subtree

:general
(dired-mode-map
:states 'normal
"i" 'dired-subtree-toggle)
:config
(advice-add 'dired-subtree-toggle
:after (lambda () (interactive)
(when all-the-icons-dired-mode
(revert-buffer)))))






9.3. persistent scratch




(use-package persistent-scratch

:hook
(org-mode . (lambda ()
"only set initial-major-mode after loading org"
(setq initial-major-mode 'org-mode)))
:general
(lc/leader-keys
"bs" '((lambda ()
"Load persistent-scratch if not already loaded"
(interactive)
(progn
(unless (boundp 'persistent-scratch-mode)
(require 'persistent-scratch))
(pop-to-buffer "*scratch*")))
:wk "scratch"))
:init
(setq persistent-scratch-autosave-interval 60)
:config
(persistent-scratch-setup-default))





9.4. rainbow parenthesis




(use-package rainbow-delimiters

:hook ((emacs-lisp-mode . rainbow-delimiters-mode)
(clojure-mode . rainbow-delimiters-mode))
)





9.5. restart-emacs




  (use-package restart-emacs

:general
(lc/leader-keys
"R" '(restart-emacs :wk "restart"))
)





9.6. term




(use-package term

:if lc/is-ipad
:straight (:type built-in)
:general
(lc/leader-keys
"'" (lambda () (interactive) (term "/bin/zsh")))
)

(use-package term
:if lc/is-windows
:straight (:type built-in)
:general
(lc/leader-keys
"'" (lambda () (interactive)
(let ((explicit-shell-file-name "C:/Program Files/Git/bin/bash"))
(call-interactively 'shell))))
;; (setq explicit-shell-file-name "C:/Program Files/Git/bin/bash")
;; (setq explicit-bash.exe-args '("--login" "-i"))
)






9.7. tramp




  • Call e.g. dired and input /ssh:user@hostname:/path/to/file

  • In .ssh/config you can set ControlMaster Yes for a host, then ssh with the terminal


(use-package tramp

:straight (:type built-in)
:init
;; Disable version control on tramp buffers to avoid freezes.
(setq vc-ignore-dir-regexp
(format "\\(%s\\)\\|\\(%s\\)"
vc-ignore-dir-regexp
tramp-file-name-regexp))
(setq tramp-default-method "ssh")
(setq tramp-auto-save-directory
(expand-file-name "tramp-auto-save" user-emacs-directory))
(setq tramp-persistency-file-name
(expand-file-name "tramp-connection-history" user-emacs-directory))
(setq password-cache-expiry nil)
(setq tramp-use-ssh-controlmaster-options nil)
(setq remote-file-name-inhibit-cache nil)
:config
(customize-set-variable 'tramp-ssh-controlmaster-options
(concat
"-o ControlPath=/tmp/ssh-tramp-%%r@%%h:%%p "
"-o ControlMaster=auto -o ControlPersist=yes"))
(with-eval-after-load 'lsp-mode
(lsp-register-client
(make-lsp-client :new-connection (lsp-tramp-connection "pyright")
:major-modes '(python-mode)
:remote? t
:server-id 'pyright-remote))
)
)

(use-package docker-tramp)






9.8. undo fu




(use-package undo-fu

:demand
:general
(:states 'normal
"u" 'undo-fu-only-undo
"s-z" 'undo-fu-only-undo
"\C-r" 'undo-fu-only-redo))





9.9. git   git






9.9.1. magit




50/72 rule for commit messages: https://www.midori-global.com/blog/2018/04/02/git-50-72-rule


magit-cycle-margin-style to show more precise commit timestamps


On iPad, we may need to (require 'sendmail) before calling magit-status


(use-package magit

:general
(lc/leader-keys
"g b" 'magit-blame
"g g" 'magit-status
"g G" 'magit-status-here
"g l" 'magit-log)
(general-nmap
:keymaps '(magit-status-mode-map
magit-stash-mode-map
magit-revision-mode-map
magit-process-mode-map
magit-diff-mode-map)
"TAB" #'magit-section-toggle
"<escape>" #'transient-quit-one)
:init
(setq magit-display-buffer-function #'magit-display-buffer-same-window-except-diff-v1)
(setq magit-log-arguments '("--graph" "--decorate" "--color"))
(setq git-commit-fill-column 72)
;; (setq magit-log-margin (t "%Y-%m-%d %H:%M " magit-log-margin-width t 18))
;; (when lc/is-ipad (require 'sendmail))
:config
(setq magit-buffer-name-format (concat "*" magit-buffer-name-format "*"))
(evil-define-key* '(normal visual) magit-mode-map
"zz" #'evil-scroll-line-to-center)
)





9.9.2. git-timemachine




  (use-package git-timemachine

:hook (git-time-machine-mode . evil-normalize-keymaps)
:init (setq git-timemachine-show-minibuffer-details t)
:general
(general-nmap "SPC g t" 'git-timemachine-toggle)
(git-timemachine-mode-map
"C-k" 'git-timemachine-show-previous-revision
"C-j" 'git-timemachine-show-next-revision
"q" 'git-timemachine-quit))





9.9.3. diff-hl




When an heading includes a change, the org-ellipsis not shown correctly.
This is caused by an empty line with diff-hl fringe that gets appended to the heading.
To work around this and show the ellipsis, you have to add a whitespace in that empty line.


(use-package diff-hl

:demand
:general
(lc/leader-keys
"g n" '(diff-hl-next-hunk :wk "next hunk")
"g p" '(diff-hl-previous-hunk :wk "prev hunk"))
:hook
((magit-pre-refresh . diff-hl-magit-pre-refresh)
(magit-post-refresh . diff-hl-magit-post-refresh))
:init
(setq diff-hl-draw-borders nil)
;; (setq diff-hl-global-modes '(not org-mode))
;; (setq diff-hl-fringe-bmp-function 'diff-hl-fringe-bmp-from-type)
;; (setq diff-hl-global-modes (not '(image-mode org-mode)))
:config
(global-diff-hl-mode)
)


:CUSTOMID: h:6C6EA9DD-42EE-49CD-A7FC-5BE9FAB42F7F






9.9.4. smerge + hydra-smerge




(use-package hydra

:after evil
:demand
:general
(lc/leader-keys "w w" 'evil-windows-hydra/body)
:init
(defhydra evil-windows-hydra (:hint nil
;; :pre (smerge-mode 1)
;; :post (smerge-auto-leave)
)
"
[_h_] ⇢⇠ decrease width [_l_] ⇠⇢ increase width
[_j_] decrease height [_k_] increase height
│ [_q_] quit"
("h" evil-window-decrease-width)
("l" evil-window-increase-width)
("j" evil-window-decrease-height)
("k" evil-window-increase-height)
("q" nil :color blue)
)
)

(use-package smerge-mode
:straight (:type built-in)
:after hydra
:general
(lc/leader-keys "g m" 'smerge-hydra/body)
:hook
(magit-diff-visit-file . (lambda ()
(when smerge-mode
(smerge-hydra/body))))
:init
(defhydra smerge-hydra (:hint nil
:pre (smerge-mode 1)
;; Disable `smerge-mode' when quitting hydra if
;; no merge conflicts remain.
:post (smerge-auto-leave))
"
╭────────┐
Movement Keep Diff Other │ smerge │
╭─────────────────────────────────────────────────┴────────╯
^_g_^ [_b_] base [_<_] upper/base [_C_] Combine
^_C-k_^ [_u_] upper [_=_] upper/lower [_r_] resolve
^_k_ ↑^ [_l_] lower [_>_] base/lower [_R_] remove
^_j_ ↓^ [_a_] all [_H_] hightlight
^_C-j_^ [_RET_] current [_E_] ediff ╭──────────
^_G_^ │ [_q_] quit"
("g" (progn (goto-char (point-min)) (smerge-next)))
("G" (progn (goto-char (point-max)) (smerge-prev)))
("C-j" smerge-next)
("C-k" smerge-prev)
("j" next-line)
("k" previous-line)
("b" smerge-keep-base)
("u" smerge-keep-upper)
("l" smerge-keep-lower)
("a" smerge-keep-all)
("RET" smerge-keep-current)
("\C-m" smerge-keep-current)
("<" smerge-diff-base-upper)
("=" smerge-diff-upper-lower)
(">" smerge-diff-base-lower)
("H" smerge-refine)
("E" smerge-ediff)
("C" smerge-combine-with-next)
("r" smerge-resolve)
("R" smerge-kill-current)
("q" nil :color blue)))







9.10. emacs tree-sitter   prog_tree_sitter




(use-package tree-sitter

;; :straight (tree-sitter :host github :repo "ubolonton/emacs-tree-sitter" :depth full)
:hook (python-mode . (lambda ()
(require 'tree-sitter)
(require 'tree-sitter-langs)
(require 'tree-sitter-hl)
(tree-sitter-hl-mode)
)))

(use-package tree-sitter-langs)






9.11. envrc   direnv




Running direnv is expensive so I only do it when it is necessary. I need it in two situations:



  • python-mode

  • ob-jupyter


Instead of simply enabling envrc-mode in every org buffer, I check with the buffer includes a jupyter-python block.
In the ob-jupyter section I then load ob-jupyter only when envrc-mode is loaded and jupyter is found on the PATH


(use-package inheritenv

:straight (inheritenv :type git :host github :repo "purcell/inheritenv"))


(use-package envrc

:straight (envrc :type git :host github :repo "purcell/envrc")
:commands (envrc-mode)
:hook ((python-mode . envrc-mode)
(org-jupyter-mode . envrc-mode))
)





9.12. snippets   yasnippet






9.12.1. yasnippet




We use C-TAB to expand snippets instead of TAB .


You can have #condition: 'auto for the snippet to auto-expand.


See here to share snippets across modes


(use-package yasnippet

:general
(yas-minor-mode-map
:states 'insert
"TAB" 'nil
"C-TAB" 'yas-expand)
:hook
((prog-mode org-mode dap-ui-repl-mode vterm-mode) . yas-minor-mode)
:init
;; (setq yas-prompt-functions '(yas-ido-prompt))
(defun lc/yas-try-expanding-auto-snippets ()
(when (and (boundp 'yas-minor-mode) yas-minor-mode)
(let ((yas-buffer-local-condition ''(require-snippet-condition . auto)))
(yas-expand))))
:config
(yas-reload-all)
(add-hook 'post-command-hook #'lc/yas-try-expanding-auto-snippets)
)





9.12.2. LaTeX yasnippets




(use-package yasnippet

:config
(setq lc/greek-alphabet
'(("a" . "\\alpha")
("b" . "\\beta" )
("g" . "\\gamma")
("d" . "\\delta")
("e" . "\\epsilon")
("z" . "\\zeta")
("h" . "\\eta")
("t" . "\\theta")
("i" . "\\iota")
("k" . "\\kappa")
("l" . "\\lambda")
("m" . "\\mu")
("n" . "\\nu")
("x" . "\\xi")
("p" . "\\pi")
("r" . "\\rho")
("s" . "\\sigma")
("t" . "\\tau")
("u" . "\\upsilon")
("f" . "\\phi")
("c" . "\\chi")
("v" . "\\psi")
("g" . "\\omega")))

(setq lc/latex-greek-prefix "'")

;; The same for capitalized letters
(dolist (elem lc/greek-alphabet)
(let ((key (car elem))
(value (cdr elem)))
(when (string-equal key (downcase key))
(add-to-list 'lc/greek-alphabet
(cons
(capitalize (car elem))
(concat
(substring value 0 1)
(capitalize (substring value 1 2))
(substring value 2)))))))

(yas-define-snippets
'latex-mode
(mapcar
(lambda (elem)
(list (concat lc/latex-greek-prefix (car elem)) (cdr elem) (concat "Greek letter " (car elem))))
lc/greek-alphabet))

(setq lc/english-alphabet
'("a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"))

(dolist (elem lc/english-alphabet)
(when (string-equal elem (downcase elem))
(add-to-list 'lc/english-alphabet (upcase elem))))

(setq lc/latex-mathbb-prefix "`")

(yas-define-snippets
'latex-mode
(mapcar
(lambda (elem)
(list (concat lc/latex-mathbb-prefix elem) (concat "\\mathbb{" elem "}") (concat "Mathbb letter " elem)))
lc/english-alphabet))

(setq lc/latex-math-symbols
'(("x" . "\\times")
("." . "\\cdot")
("v" . "\\forall")
("s" . "\\sum_{$1}^{$2}$0")
("p" . "\\prod_{$1}^{$2}$0")
("e" . "\\exists")
("i" . "\\int_{$1}^{$2}$0")
("c" . "\\cap")
("u" . "\\cup")
("0" . "\\emptyset")))

(setq lc/latex-math-prefix "''")

(yas-define-snippets
'latex-mode
(mapcar
(lambda (elem)
(let ((key (car elem))
(value (cdr elem)))
(list (concat lc/latex-math-prefix key) value (concat "Math symbol " value))))
lc/latex-math-symbols))
)







9.13. vterm and friends   prog_vterm






9.13.1. vterm




(use-package vterm

:if (not lc/is-ipad)
:general
(general-imap
:keymaps 'vterm-mode-map
"M-l" 'vterm-send-right
"M-h" 'vterm-send-left)
:config
(setq vterm-shell (executable-find "fish")
vterm-max-scrollback 10000))





9.13.2. vterm toggle




NOTE:



  • You can use universal argument to create a new vterm buffer (SPC U SPC ')


(use-package vterm-toggle

:if (not lc/is-ipad)
:general
(lc/leader-keys
"'" 'vterm-toggle)
:init
(setq vterm-toggle-scope 'project)
)






9.14. search google   prog_extra




(use-package emacs

:general
(lc/leader-keys
"s g" '(google-search :wk "google"))
:init
(defun google-search-str (str)
(browse-url
(concat "https://www.google.com/search?q=" str)))
(defun google-search ()
"Google search region, if active, or ask for search string."
(interactive)
(if (region-active-p)
(google-search-str
(buffer-substring-no-properties (region-beginning)
(region-end)))
(google-search-str (read-from-minibuffer "Search: "))))
)





9.15. search github   prog_extra




(use-package emacs

:general
(lc/leader-keys
"s c" '(github-code-search :wk "code (github)"))
:init
(defun github-code-search ()
"Search code on github for a given language."
(interactive)
(let ((language (completing-read
"Language: "
'("Emacs Lisp" "Python" "Clojure" "R")))
(code (read-string "Code: ")))
(browse-url
(concat "https://github.com/search?l=" language
"&type=code&q=" code))))
)





9.16. transient help commands   prog_extra




(use-package transient

:general
(lc/leader-keys
"h h" 'lc/help-transient)
:config
(transient-define-prefix lc/help-transient ()
["Help Commands"
["Mode & Bindings"
("m" "Mode" describe-mode)
("b" "Major Bindings" which-key-show-full-major-mode)
("B" "Minor Bindings" which-key-show-full-minor-mode-keymap)
("d" "Descbinds" describe-bindings)
]
["Describe"
("c" "Command" helpful-command)
("f" "Function" helpful-callable)
("v" "Variable" helpful-variable)
("k" "Key" helpful-key)
]
["Info on"
("C-c" "Emacs Command" Info-goto-emacs-command-node)
("C-f" "Function" info-lookup-symbol)
("C-v" "Variable" info-lookup-symbol)
("C-k" "Emacs Key" Info-goto-emacs-key-command-node)
]
["Goto Source"
("L" "Library" find-library)
("F" "Function" find-function)
("V" "Variable" find-variable)
("K" "Key" find-function-on-key)
]
]
[
["Internals"
("e" "Echo Messages" view-echo-area-messages)
("l" "Lossage" view-lossage)
]
["Describe"
("s" "Symbol" helpful-symbol)
("." "At Point " helpful-at-point)
;; ("C-f" "Face" counsel-describe-face)
("w" "Where Is" where-is)
("=" "Position" what-cursor-position)
]
["Info Manuals"
("C-i" "Info" info)
("C-4" "Other Window " info-other-window)
("C-e" "Emacs" info-emacs-manual)
;; ("C-l" "Elisp" info-elisp-manual)
]
["Exit"
("q" "Quit" transient-quit-one)
("<escape>" "Quit" transient-quit-one)
]
;; ["External"
;; ("W" "Dictionary" lookup-word-at-point)
;; ("D" "Dash" dash-at-point)
;; ]
]
)
)





9.17. transient increase/decrease font size   prog_extra




(use-package transient

:general
(lc/leader-keys
"t f" 'lc/font-size-transient)
:config
(transient-define-prefix lc/font-size-transient ()
"Change font size"
["Font size"
("+" "Increase" (lambda () (interactive) (progn (text-scale-increase) (lc/font-size-transient))))
("-" "Decrease" (lambda () (interactive) (progn (text-scale-decrease) (lc/font-size-transient))))
])
)





9.18. isearch-mb   prog_extra




You can use consult-history to browse past searches


(use-package isearch-mb

:straight (isearch-mb :type git :host github :repo "astoff/isearch-mb")
:demand
:init
(setq-default
;; Match count next to minibuffer prompt
isearch-lazy-count t
;; Don't be stingy with history; default is to keep just 16 entries
search-ring-max 200
regexp-search-ring-max 200
;; fuzzy match with space
isearch-regexp-lax-whitespace t
search-whitespace-regexp ".*?"
)
:config
(add-to-list 'isearch-mb--with-buffer #'loccur-isearch)
(define-key isearch-mb-minibuffer-map (kbd "C-o") #'loccur-isearch)
)





9.19. olivetti mode   extra_focus




  (use-package olivetti

:general
(lc/leader-keys
"t o" '(olivetti-mode :wk "olivetti"))
:init
(setq olivetti-body-width 100)
(setq olivetti-recall-visual-line-mode-entry-state t))





9.20. darkroom   extra_focus




(use-package darkroom

:init
;; Don't scale the text, so ugly man!
(setq darkroom-text-scale-increase 3)
:general
(lc/leader-keys
"t F" '(darkroom-tentative-mode :wk "focus")))






10. Programming languages






10.1. lsp mode and friends   prog_lsp






10.1.1. lsp-mode




(use-package lsp-mode

:commands
(lsp lsp-deferred)
:hook
((lsp-mode . (lambda () (setq-local evil-lookup-func #'lsp-describe-thing-at-point)))
(lsp-mode . lsp-enable-which-key-integration))
:general
(lc/local-leader-keys
:states 'normal
:keymaps 'lsp-mode-map
"i" '(:ignore t :which-key "import")
"i o" '(lsp-organize-imports :wk "optimize")
"l" '(:keymap lsp-command-map :wk "lsp")
"a" '(lsp-execute-code-action :wk "code action")
"r" '(lsp-rename :wk "rename"))
;; (lsp-mode-map
;; :states 'normal
;; "gD" 'lsp-find-references)
:init
(setq lsp-restart 'ignore)
(setq lsp-eldoc-enable-hover nil)
(setq lsp-enable-file-watchers nil)
(setq lsp-signature-auto-activate nil)
(setq lsp-modeline-diagnostics-enable nil)
(setq lsp-keep-workspace-alive nil)
(setq lsp-auto-execute-action nil)
(setq lsp-before-save-edits nil)
(setq lsp-headerline-breadcrumb-enable nil)
(setq lsp-diagnostics-provider :none)
)





10.1.2. lsp-ui




(use-package lsp-ui

:hook
((lsp-mode . lsp-ui-mode)
;; (lsp-mode . (lambda () (setq-local evil-goto-definition-functions '(lambda (&rest args) (lsp-ui-peek-find-definitions)))))
)
;; :bind
;; (:map lsp-ui-mode-map
;; ([remap lsp-find-references] . lsp-ui-peek-find-references))
:general
(lc/local-leader-keys
"h" 'lsp-ui-doc-show
"H" 'lsp-ui-doc-hide)
(lsp-ui-peek-mode-map
:states 'normal
"C-j" 'lsp-ui-peek--select-next
"C-k" 'lsp-ui-peek--select-prev)
(outline-mode-map
:states 'normal
"C-j" 'nil
"C-k" 'nil)
:init
(setq lsp-ui-doc-show-with-cursor nil)
(setq lsp-ui-doc-show-with-mouse nil)
(setq lsp-ui-peek-always-show t)
(setq lsp-ui-peek-fontify 'always)
)





10.1.3. dap-mode




(use-package dap-mode

:hook
((dap-mode . corfu-mode)
(dap-terminated . lc/hide-debug-windows)
(dap-session-created . (lambda (_arg) (projectile-save-project-buffers)))
(dap-ui-repl-mode . (lambda () (setq-local truncate-lines t))))
:general
(lc/local-leader-keys
:states '(normal)
:keymaps '(python-mode-map dap-ui-repl-mode-map)
"d d" '(dap-debug :wk "debug")
"d b" '(dap-breakpoint-toggle :wk "breakpoint toggle")
"d B" '(dap-ui-breakpoints-list :wk "breakpoint list")
"d c" '(dap-continue :wk "continue")
"d n" '(dap-next :wk "next")
"d e" '(dap-eval-thing-at-point :wk "eval")
"d i" '(dap-step-in :wk "step in")
"d l" '(dap-debug-last :wk "step in")
"d q" '(dap-disconnect :wk "quit")
"d r" '(dap-ui-repl :wk "repl")
"d h" '(dap-hydra :wk "hydra")
"d i" '(lc/dap-inspect-df :wk "view df")
"d I" '(lc/dap-inspect-df2 :wk "view df2")
;; "d t" '(lc/dap-dtale-df :wk "dtale df")
)
(:keymaps 'dap-ui-repl-mode-map
"<backtab>" 'dabbrev-completion
"TAB" 'lc/py-indent-or-complete)
:init
;; (defun lc/dap-dtale-df (dataframe)
;; "Show df in tale in default browser"
;; (interactive (list (read-from-minibuffer "DataFrame: " (evil-find-symbol nil))))
;; (dap-eval (concat "import dtale; dtale.show(" dataframe ", open_browser=True)")))
(setq lc/dap-temp-dataframe-buffer "*inspect-df*")
(setq lc/dap-temp-dataframe-path "~/tmp-inspect-df.csv")
(defun lc/dap-inspect-df (dataframe)
"Save the df to csv and open the file with csv-mode"
(interactive (list (read-from-minibuffer "DataFrame: " (evil-find-symbol nil))))
(dap-eval (format "%s.to_csv('%s', index=False)" dataframe lc/dap-temp-dataframe-path))
(sleep-for 1)
(find-file-other-window lc/dap-temp-dataframe-path)
)
(defun lc/dap-inspect-df2 (dataframe)
"Save the df to csv and open the file with csv-mode"
(interactive (list (read-from-minibuffer "DataFrame: " (evil-find-symbol nil))))
(dap-eval (concat dataframe ".to_csv('~/tmp-inspect-df2.csv', index=False)"))
(sleep-for 1)
(with-current-buffer
(display-buffer
(with-current-buffer (find-file-noselect "~/tmp-inspect-df2.csv")
(rename-buffer "*inspect-df2*"))
'((;display-buffer-in-side-window
display-buffer-reuse-window)
(side . right)
(window-width . 0.5)
)))
)
;; prevent minibuffer prompt about reloading from disk
(setq revert-without-query '("~/tmp-inspect-df.csv"))
;; (setq dap-auto-configure-features '(locals repl))
(setq dap-auto-configure-features '(sessions repl))
(setq dap-python-debugger 'debugpy)
;; show stdout
(setq dap-auto-show-output t)
(setq dap-output-window-min-height 10)
(setq dap-output-window-max-height 200)
(setq dap-overlays-use-overlays nil)
;; hide stdout window when done
(defun lc/hide-debug-windows (session)
"Hide debug windows when all debug sessions are dead."
(unless (-filter 'dap--session-running (dap--get-sessions))
;; delete output buffer
(when-let (window (get-buffer-window (dap--debug-session-output-buffer (dap--cur-session-or-die))))
(delete-window window))
;; delete dataframe inspector window
(when-let
(window (get-buffer-window (get-file-buffer lc/dap-temp-dataframe-path)))
(delete-window window)))
)
(defun lc/dap-python--executable-find (orig-fun &rest args)
(executable-find "python"))
:config
;; configure windows
(require 'dap-ui)
(setq dap-ui-buffer-configurations
'(("*dap-ui-sessions*"
(side . bottom)
(slot . 1)
(window-height . 0.33))
("*debug-window*"
(side . bottom)
(slot . 2)
(window-height . 0.33))
("*dap-ui-repl*"
(side . bottom)
(slot . 3)
(window-height . 0.33))))
(dap-ui-mode 1)
;; python virtualenv
(require 'dap-python)
(advice-add 'dap-python--pyenv-executable-find :around #'lc/dap-python--executable-find)
;; debug templates
(defvar dap-script-args (list :type "python"
:args []
:cwd "${workspaceFolder}"
:justMyCode :json-false
:request "launch"
:debugger 'debugpy
:name "dap-debug-script"))
(defvar dap-test-args (list :type "python-test-at-point"
:args ""
:justMyCode :json-false
;; :cwd "${workspaceFolder}"
:request "launch"
:module "pytest"
:debugger 'debugpy
:name "dap-debug-test-at-point"))
(defvar flight-tower-mill (list
:name "mill"
:type "python"
:request "launch"
:program (expand-file-name "~/git/Sodra.Common.FlightTower/flight_tower/__main__.py")
;; :env '(("NO_JSON_LOG" . "true"))
:args ["-m" "mill" "--config" "user_luca"]))
(defvar flight-tower-calibration (list
:name "mill"
:type "python"
:request "launch"
:program (expand-file-name "~/git/Sodra.Common.FlightTower/flight_tower/__main__.py")
;; :env '(("NO_JSON_LOG" . "true"))
:args ["-m" "mill"
;; "--config" "user_luca"
;; "--config" "calibration_g292imp_41x185"
;; "--config" "calibration_41x185_38x89"
"--config" "calibration_jan22"
]
))
(defvar flight-tower-e2e (list
:name "mill"
:type "python"
:request "launch"
:program (expand-file-name "~/git/Sodra.Common.FlightTower/flight_tower/__main__.py")
;; :env '(("NO_JSON_LOG" . "true"))
:args ["-m" "wood_processing_e2e"
"--config" "user_luca"]
))
(dap-register-debug-template "dap-debug-script" dap-script-args)
(dap-register-debug-template "dap-debug-test-at-point" dap-test-args)
(dap-register-debug-template "flight-tower-mill" flight-tower-mill)
(dap-register-debug-template "flight-tower-e2e" flight-tower-e2e)
(dap-register-debug-template "flight-tower-calibration" flight-tower-calibration)
;; bind the templates
(lc/local-leader-keys
:keymaps 'python-mode-map
"d t" '((lambda () (interactive) (dap-debug dap-test-args)) :wk "test")
"d s" '((lambda () (interactive) (dap-debug dap-script-args)) :wk "script")
)
)






10.2. Python   prog_python






10.2.1. python mode




NOTE:



  • In a python buffer use run-python to start an inferior python process or ,'

  • Use eval operator so eval lines with grr


(use-package python-mode

:hook
((envrc-mode . (lambda ()
(when (executable-find "ipython")
(setq python-shell-interpreter (executable-find "ipython"))))))
:general
(lc/local-leader-keys
:keymaps 'python-mode-map
"'" 'run-python)
(python-mode-map
:states 'normal
"gz" nil
"C-j" nil)
(python-mode-map
:states 'insert
"TAB" 'lc/py-indent-or-complete)
:init
(setq python-indent-offset 0)
(defun lc/py-indent-or-complete ()
(interactive "*")
(window-configuration-to-register py--windows-config-register)
(cond ((use-region-p)
(py-indent-region (region-beginning) (region-end)))
((or (bolp)
(member (char-before) (list 9 10 12 13 32 ?: ;; ([{
?\) ?\] ?\}))
;; (not (looking-at "[ \t]*$"))
)
(py-indent-line))
((comint-check-proc (current-buffer))
(ignore-errors (completion-at-point)))
(t
(completion-at-point))))
:config
(setq python-shell-interpreter-args "-i --simple-prompt --no-color-info"
python-shell-prompt-regexp "In \\[[0-9]+\\]: "
python-shell-prompt-block-regexp "\\.\\.\\.\\.: "
python-shell-prompt-output-regexp "Out\\[[0-9]+\\]: "
python-shell-completion-setup-code
"from IPython.core.completerlib import module_completion"
python-shell-completion-string-code
"';'.join(get_ipython().Completer.all_completions('''%s'''))\n")
)





10.2.2. lsp-pyright




Here the configuration options: https://github.com/emacs-lsp/lsp-pyright#configuration



(use-package lsp-pyright

:init
(setq lsp-pyright-typechecking-mode "basic") ;; too much noise in "real" projects
:hook (python-mode . (lambda ()
(require 'lsp-pyright)
(lsp-deferred))))





10.2.3. pytest




(use-package python-pytest

:general
(lc/local-leader-keys
:keymaps 'python-mode-map
"t" '(:ignore t :wk "test")
"t d" '(python-pytest-dispatch :wk "dispatch")
"t f" '(python-pytest-file :wk "file")
"t t" '(python-pytest-function :wk "function"))
:init
(setq python-pytest-arguments '("--color" "--failed-first"))
(defun lc/pytest-use-venv (orig-fun &rest args)
(if-let ((python-pytest-executable (executable-find "pytest")))
(apply orig-fun args)
(apply orig-fun args)))
:config
(advice-add 'python-pytest--run :around #'lc/pytest-use-venv)
)





10.2.4. flycheck




flycheck-verify-setup is your best friend.


(use-package flycheck

:hook ((lsp-mode . flycheck-mode)
(envrc-mode . (lambda ()
(setq flycheck-python-flake8-executable (executable-find "python"))
(setq flycheck-checker 'python-flake8)
(setq flycheck-flake8rc ".flake8")
)))
:init
(setq flycheck-indication-mode 'right-fringe)
;; only check on save
(setq flycheck-check-syntax-automatically '(mode-enabled save))
)





10.2.5. blacken




(use-package blacken

:general
(lc/local-leader-keys
:keymaps 'python-mode-map
"=" '(blacken-buffer :wk "format"))
)





10.2.6. csv mode




(use-package csv-mode

:hook (csv-mode . lc/init-csv-mode)
:general
(lc/local-leader-keys
:keymaps 'csv-mode-map
:states 'normal
"a" '(csv-align-fields :wk "align fields")
"A" '(lc/csv-align-visible :wk "align fields, visible")
"i" '(lc/init-csv-mode :wk "init csv mode")
"u" '(csv-unalign-fields :wk "unalign fields")
"s" '(csv-sort-fields :wk "sort fields")
";" '(lc/set-csv-semicolon-separator :wk "set semicolon sep")
"," '(lc/reset-csv-separators :wk "set comma sep"))
:init
(defun lc/csv-align-visible (&optional arg)
"Align visible fields"
(interactive "P")
(csv-align-fields nil (window-start) (window-end)))
(defun lc/set-csv-semicolon-separator ()
(interactive)
(customize-set-variable 'csv-separators '(";")))
(defun lc/reset-csv-separators ()
(interactive)
(customize-set-variable 'csv-separators lc/default-csv-separators))
(defun lc/init-csv-mode ()
(interactive)
(lc/set-csv-separators)
(lc/csv-highlight)
(call-interactively 'csv-align-fields))
:config
(require 'cl)
(require 'color)
(defun lc/set-csv-separators ()
(interactive)
(let* ((n-commas (count-matches "," (point-at-bol) (point-at-eol)))
(n-semicolons (count-matches ";" (point-at-bol) (point-at-eol))))
(if ( ; <
> n-commas n-semicolons)
(customize-set-variable 'csv-separators '("," " "))
(customize-set-variable 'csv-separators '(";" " ")))))
(defun lc/csv-highlight ()
(interactive)
(font-lock-mode 1)
(let* ((separator (string-to-char (car csv-separators)))
(n (count-matches (string separator) (point-at-bol) (point-at-eol)))
(colors (loop for i from 0 to 1.0 by (/ 2.0 n)
collect (apply #'color-rgb-to-hex
(color-hsl-to-rgb i 0.3 0.5)))))
(loop for i from 2 to n by 2
for c in colors
for r = (format "^\\([^%c\n]+%c\\)\\{%d\\}" separator separator i)
do (font-lock-add-keywords nil `((,r (1 '(face (:foreground ,c)))))))))
)






10.3. Jupyter   prog_jupyter






10.3.1. jupyter




zmq installation:



  • Need to have automake, autoconf

  • In straight/build/zmq/src run autoreconf -i

  • In straight/build/zmq run make


emacs-zmq installation:



  • In straight/build/emacs-zmq run wget https://github.com/nnicandro/emacs-zmq/releases/download/v0.10.10/emacs-zmq-x86_64-apple-darwin17.4.0.tar.gz

  • Then tar -xzf emacs-zmq-x86_64-apple-darwin17.4.0.tar.gz

  • Finally cp emacs-zmq-x86_64-apple-darwin17.4.0/emacs-zmq.so emacs-zmq.dylib
  • In the REPL you can use M-p / M-n to navigate previous prompts


(use-package jupyter

:straight (:no-native-compile t :no-byte-compile t) ;; otherwise we get jupyter-channel void
:general
(lc/local-leader-keys
:keymaps 'python-mode-map
"e" '(:ignore true :wk "eval")
"e e" '(jupyter-eval-line-or-region :wk "line")
"e d" '(jupyter-eval-defun :wk "defun")
"e b" '((lambda () (interactive) (lc/jupyter-eval-buffer)) :wk "buffer")
"J" '(lc/jupyter-repl :wk "jupyter REPL")
"k" '(:ignore true :wk "kernel"))
;; (lc/local-leader-keys
;; :keymaps 'python-mode-map
;; :states 'visual
;; "e" '(jupyter-eval-region :wk "eval"))
(lc/local-leader-keys
:keymaps 'jupyter-org-interaction-mode-map
:states 'normal
"k i" '(jupyter-org-interrupt-kernel :wk "interrupt")
"k r" '(jupyter-repl-restart-kernel :wk "restart"))
:init
(setq jupyter-repl-prompt-margin-width 4)
(defun jupyter-command-venv (&rest args)
"This overrides jupyter-command to use the virtualenv's jupyter"
(let ((jupyter-executable (executable-find "jupyter")))
(with-temp-buffer
(when (zerop (apply #'process-file jupyter-executable nil t nil args))
(string-trim-right (buffer-string))))))
(defun lc/jupyter-eval-buffer ()
"Send the contents of BUFFER using `jupyter-current-client'."
(interactive)
(jupyter-eval-string (jupyter-load-file-code (buffer-file-name))))
(defun lc/jupyter-repl ()
"If a buffer is already associated with a jupyter buffer, then pop to it. Otherwise start a jupyter kernel."
(interactive)
(if (bound-and-true-p jupyter-current-client)
(jupyter-repl-pop-to-buffer)
(call-interactively 'jupyter-repl-associate-buffer)))
(advice-add 'jupyter-command :override #'jupyter-command-venv))





10.3.2. ob-jupyter




Note:



  • We can only load ob-jupyter when we have jupyter on our PATH.

    • We assume jupyter is always installed in a virtual env associated with an .envrc file

    • We load jupyter when we activate envrc-mode if jupyter is available




When exporting we can add this to the header:


When exporting .org file to HTML, we can add this header:



#+HTML_HEAD: <link rel="stylesheet" type="text/css" href="https://gongzhitaao.org/orgcss/org.css"/>



We can avoid evaluation of code with:




(use-package jupyter

:straight (:no-native-compile t :no-byte-compile t) ;; otherwise we get jupyter-channel void
:general
(lc/local-leader-keys
:keymaps 'org-mode-map
"=" '((lambda () (interactive) (jupyter-org-insert-src-block t nil)) :wk "block below")
"m" '(jupyter-org-merge-blocks :wk "merge")
"+" '(jupyter-org-insert-src-block :wk "block above")
"?" '(jupyter-inspect-at-point :wk "inspect")
"x" '(jupyter-org-kill-block-and-results :wk "kill block"))
:hook ((jupyter-org-interaction-mode . (lambda () (lc/add-local-electric-pairs '((?' . ?')))))
(jupyter-repl-persistent-mode . (lambda () ;; we activate org-interaction-mode ourselves
(when (derived-mode-p 'org-mode)
;; (setq-local company-backends '((company-capf)))
(setq-local evil-lookup-func #'jupyter-inspect-at-point)
(jupyter-org-interaction-mode))))
(envrc-mode . lc/load-ob-jupyter))
:init
(setq org-babel-default-header-args:jupyter-python '((:async . "yes")
(:pandoc t)
(:kernel . "python3")))
(setq org-babel-default-header-args:jupyter-R '((:pandoc t)
(:async . "yes")
(:kernel . "ir")))
(defun lc/org-load-jupyter ()
(org-babel-do-load-languages 'org-babel-load-languages
(append org-babel-load-languages
'((jupyter . t)))))
(defun lc/load-ob-jupyter ()
;; only try to load in org-mode
(when (derived-mode-p 'org-mode)
;; skip if already loaded
(unless (member '(jupyter . t) org-babel-load-languages)
;; only load if jupyter is available
(when (executable-find "jupyter")
(lc/org-load-jupyter)))))
:config
(cl-defmethod jupyter-org--insert-result (_req context result)
(let ((str
(org-element-interpret-data
(jupyter-org--wrap-result-maybe
context (if (jupyter-org--stream-result-p result)
(thread-last result
jupyter-org-strip-last-newline
jupyter-org-scalar)
result)))))
(if (< (length str) 100000) ;; >
(insert str)
(insert (format ": Result was too long! Length was %d" (length str)))))
(when (/= (point) (line-beginning-position))
;; Org objects such as file links do not have a newline added when
;; converting to their string representation by
;; `org-element-interpret-data' so insert one in these cases.
(insert "\n")))
;;Remove text/html since it's not human readable
;; (delete :text/html jupyter-org-mime-types)
;; (with-eval-after-load 'org-src
;; (add-to-list 'org-src-lang-modes '("jupyter-python" . python))
;; (add-to-list 'org-src-lang-modes '("jupyter-R" . R)))
)






10.4. R   prog_R






10.4.1. ess




(use-package ess

:general
(lc/local-leader-keys
:keymaps 'ess-r-mode-map
:states 'normal
"R" '(R :wk "R")
"q" '(ess-quit :wk "quit")
"RET" '(ess-eval-line-visibly-and-step :wk "line and step")
;; debug
"d b" '(ess-bp-set :wk "breakpoint")
"d n" '(ess-debug-command-next :wk "next")
"d q" '(ess-debug-command-quit :wk "quit")
"d c" '(ess-bp-next :wk "continue")
"d f" '(ess-debug-flag-for-debugging :wk "flag function")
"d F" '(ess-debug-unflag-for-debugging :wk "unflag function")
"d p" '(ess-debug-goto-debug-point :wk "go to point")
;; "e l" '(ess-eval-line :wk "eval line")
"e p" '(ess-eval-paragraph :wk "paragraph")
"e f" '(ess-eval-function :wk "function")
"h" '(:keymap ess-doc-map :which-key "help")
;; "h" '(ess-display-help-on-object :wk "help")
)
(lc/local-leader-keys
:keymaps 'ess-r-mode-map
:states 'visual
"RET" '(ess-eval-region-or-line-visibly-and-step :wk "line and step"))
:init
(setq ess-eval-visibly 'nowait)
(setq ess-R-font-lock-keywords '((ess-R-fl-keyword:keywords . t)
(ess-R-fl-keyword:constants . t)
(ess-R-fl-keyword:modifiers . t)
(ess-R-fl-keyword:fun-defs . t)
(ess-R-fl-keyword:assign-ops . t)
(ess-R-fl-keyword:%op% . t)
(ess-fl-keyword:fun-calls . t)
(ess-fl-keyword:numbers . t)
(ess-fl-keyword:operators . t)
(ess-fl-keyword:delimiters . t)
(ess-fl-keyword:= . t)
(ess-R-fl-keyword:F&T . t)))
;; (setq ess-first-continued-statement-offset 2
;; ess-continued-statement-offset 0
;; ess-expression-offset 2
;; ess-nuke-trailing-whitespace-p t
;; ess-default-style 'DEFAULT)
;; (setq ess-r-flymake-linters "line_length_linter = 120")
)





10.4.2. ESS data viewer




(use-package ess-view-data

:general
(lc/local-leader-keys
:keymaps 'ess-r-mode-map
:states 'normal
"hd" 'ess-R-dv-pprint
"ht" 'ess-R-dv-ctable
))





10.4.3. enable lsp-mode




(use-package lsp-mode

:hook
(ess-r-mode . lsp-deferred)
)






10.5. emacs-lisp   prog_elisp






10.5.1. emacs-lisp-mode




(use-package emacs

:straight (:type built-in)
:general
(general-nmap
:keymaps 'emacs-lisp-mode-map
:states 'normal
"gr" nil) ;; interferes with eval-operator
)





10.5.2. evil-lisp state




NOTE:



  • Wrap with SPC l w

  • Raise with SPC l r

  • Enter lisp-state with SPC l .

  • Navigate symbols with j and k

  • Navigate forms with h and l

  • Go to parent sexp with U


(use-package evil-lisp-state

:after evil
:demand
:init
(setq evil-lisp-state-enter-lisp-state-on-command nil)
(setq evil-lisp-state-global t)
;; (setq evil-lisp-state-major-modes '(org-mode emacs-lisp-mode clojure-mode clojurescript-mode lisp-interaction-mode))
:config
(evil-lisp-state-leader "SPC l")
)





10.5.3. eros: results in overlays




(use-package eros

:hook ((emacs-lisp-mode org-mode lisp-interaction-mode) . eros-mode)
:general
(lc/local-leader-keys
:keymaps '(org-mode-map emacs-lisp-mode-map lisp-interaction-mode-map)
:states 'normal
"e l" '(eros-eval-last-sexp :wk "last sexp")
;; "e d" '((lambda () (interactive) (eros-eval-defun t)) :wk "defun")
"e b" '(eval-buffer :wk "buffer"))
(lc/local-leader-keys
:keymaps '(org-mode-map emacs-lisp-mode-map lisp-interaction-mode-map)
:states 'visual
;; "e" '((lambda (start end)
;; (interactive (list (region-beginning) (region-end)))
;; (eval-region start end t))
;; :wk "region")
;; "e" '((lambda (start end)
;; (interactive (list (region-beginning) (region-end)))
;; (eros--eval-overlay
;; (eval-region start end)
;; end))
;; :wk "region")
"e" '(eros-eval-region :wk "region")
)
:init
(defun eros-eval-region (start end)
(interactive "r")
(eros--eval-overlay
(string-trim
(with-output-to-string
(eval-region start end standard-output)))
(max (point) (mark))))
)






10.6. Nix   prog_nix






10.6.1. nix mode




(use-package nix-mode

:mode "\\.nix\\'")






10.7. Clojure   prog_clojure






10.7.1. Clojure mode




(use-package clojure-mode

:mode "\\.clj$"
:init
(setq clojure-align-forms-automatically t))





10.7.2. clojure-lsp




(use-package clojure-mode

:hook
((clojure-mode clojurescript-mode)
. (lambda ()
(setq-local lsp-enable-indentation nil ; cider indentation
lsp-enable-completion-at-point nil ; cider completion
)
(lsp-deferred)))
)





10.7.3. Cider




(use-package cider

:hook ((cider-repl-mode . evil-normalize-keymaps)
(cider-mode . (lambda ()
(setq-local evil-lookup-func #'cider-doc)))
(cider-mode . eldoc-mode))
:general
(lc/local-leader-keys
:keymaps 'clojure-mode-map
"c" '(cider-connect-clj :wk "connect")
"C" '(cider-connect-cljs :wk "connect (cljs)")
"j" '(cider-jack-in :wk "jack in")
"J" '(cider-jack-in-cljs :wk "jack in (cljs)")
"d d" 'cider-debug-defun-at-point
"e b" 'cider-eval-buffer
"e l" 'cider-eval-last-sexp
"e L" 'cider-pprint-eval-last-sexp-to-comment
"e d" '(cider-eval-defun-at-point :wk "defun")
"e D" 'cider-pprint-eval-defun-to-comment
"h" 'cider-clojuredocs-web
"K" 'cider-doc
"q" '(cider-quit :qk "quit")
)
(lc/local-leader-keys
:keymaps 'clojure-mode-map
:states 'visual
"e" 'cider-eval-region)
:init
(setq nrepl-hide-special-buffers t)
(setq nrepl-sync-request-timeout nil)
(setq cider-repl-display-help-banner nil)
)





10.7.4. ob-clojure




(use-package org

:config
(require 'ob-clojure)
(setq org-babel-clojure-backend 'cider))





10.7.5. aggressive-indent




  ;; keep the file indented

(use-package aggressive-indent
:hook ((clojure-mode . aggressive-indent-mode)
(emacs-lisp-mode . aggressive-indent-mode)))






10.8. markdown   prog_markdown




(use-package markdown-mode

:commands (markdown-mode gfm-mode)
:mode (("README\\.md\\'" . gfm-mode)
("\\.md\\'" . markdown-mode)
("\\.markdown\\'" . markdown-mode))
:init (setq markdown-command "multimarkdown"))





10.9. yaml mode   prog_markdown




(use-package yaml-mode

:mode ((rx ".yml" eos) . yaml-mode))





10.10. toml mode   prog_markdown




(use-package toml-mode

:mode "\\.toml\\'")






11. Gimmicks






11.1. web browser   extra_web




Use SPC x x to search the web! You can also visit a URL in the buffer.
With the web browser open, scroll with j / k .
To visit a link, SPC x l



(use-package xwwp

:straight (xwwp :type git :host github :repo "canatella/xwwp")
:commands (xwwp)
:general
(lc/leader-keys
"x x" '((lambda () (interactive)
(let ((current-prefix-arg 4)) ;; emulate C-u universal arg
(call-interactively 'xwwp)))
:wk "search or visit")
"x l" '(xwwp-follow-link :wk "link")
"x b" '(xwidget-webkit-back :wk "back"))
;; :custom
;; (setq xwwp-follow-link-completion-system 'ivy)
;; :bind (:map xwidget-webkit-mode-map
;; ("v" . xwwp-follow-link))
)





11.2. elfeed   extra_rss




NOTE:



  • Search with s

  • Open in browser with S-RET

  • Open in xwwp with x


(use-package elfeed

:straight (elfeed :type git :host github :repo "skeeto/elfeed")
:hook (elfeed-search-mode . elfeed-update)
:general
(lc/leader-keys
"s r" '(elfeed :wk "elfeed"))
(general-nmap
:keymaps 'elfeed-search-mode-map
"x" 'lc/elfeed-xwwp-open)
:init
(defun lc/elfeed-xwwp-open (&optional use-generic-p)
"open with eww"
(interactive "P")
(let ((entries (elfeed-search-selected)))
(cl-loop for entry in entries
do (elfeed-untag entry 'unread)
when (elfeed-entry-link entry)
do (xwwp it))
(mapc #'elfeed-search-update-entry entries)
(unless (use-region-p) (forward-line))))
:config
(setq elfeed-feeds'(("https://www.reddit.com/r/emacs.rss?sort=new" reddit emacs)
("http://emacsredux.com/atom.xml" emacs)
("http://irreal.org/blog/?tag=emacs&amp;feed=rss2" emacs)
("https://www.reddit.com/search.rss?q=url%3A%28youtu.be+OR+youtube.com%29&sort=top&t=week&include_over_18=1&type=link"
reddit youtube popular))))






12. Provide modules






12.1. init-core




(provide 'init-core)

;;; init-core.el ends here





12.2. init-ui-extras




(provide 'init-ui-extra)

;;; init-ui-extra.el ends here





12.3. init-org-export




(provide 'init-org-export)

;;; init-org-export.el ends here





12.4. init-prog-tree-sitter




(provide 'init-prog-tree-sitter)

;;; init-prog-tree-sitter.el ends here





12.5. init-prog-nix




(provide 'init-prog-nix)

;;; init-prog-nix.el ends here





12.6. init-prog-lsp




(provide 'init-prog-lsp)

;;; init-prog-lsp.el ends here





12.7. init-prog-python




(provide 'init-prog-python)

;;; init-prog-python.el ends here





12.8. init-prog-jupyter




(provide 'init-prog-jupyter)

;;; init-prog-jupyter.el ends here





12.9. init-org-roam




(provide 'init-org-roam)

;;; init-org-roam.el ends here





12.10. init-prog-elisp




(provide 'init-prog-elisp)

;;; init-org-prog-elisp.el ends here





12.11. init-prog-r




(provide 'init-prog-r)

;;; init-org-prog-r.el ends here





12.12. init-prog-clojure




(provide 'init-prog-clojure)

;;; init-org-prog-clojure.el ends here





12.13. init-prog-vterm




(provide 'init-prog-vterm)

;;; init-prog-vterm.el ends here





12.14. init-prog-markdown




(provide 'init-prog-markdown)

;;; init-prog-markdown.el ends here





12.15. init-extra-focus




(provide 'init-extra-focus)

;;; init-org-extra-focus.el ends here





12.16. init-extra-web




(provide 'init-extra-web)

;;; init-org-extra-web.el ends here





12.17. init-extra-rss




(provide 'init-extra-rss)

;;; init-org-extra-rss.el ends here





12.18. init-extras




(provide 'init-extra)

;;; init-extra.el ends here






<!--/*--><![CDATA[/*><!--*/
let cookie = document.cookie.split('; ').find(r => r.startsWith('org_html_themify_theme'))
let theme = 'light'

if (cookie) {
theme = cookie.split('=')[1]
}

let toggleThemeBtn = document.getElementById('toggle-theme')
let toggleTocBtn = document.getElementById('toggle-toc')

if (theme == 'light') {
useLightTheme();
} else {
useDarkTheme();
}

function transition() {
document.body.classList.add('theme-transition')
setTimeout(() => document.body.classList.remove('theme-transition'), 500)
}

function useLightTheme(theme) {
document.body.dataset.theme = 'light'
toggleThemeBtn.innerHTML = 'dark theme'
document.cookie = 'org_html_themify_theme=light'
}

function useDarkTheme(theme) {
document.body.dataset.theme = 'dark'
toggleThemeBtn.innerHTML = 'light theme'
document.cookie = 'org_html_themify_theme=dark'
}

toggleThemeBtn.addEventListener('click', function() {
transition()
if (document.body.dataset.theme == 'light') {
useDarkTheme();
} else {
useLightTheme();
}
})

let toc = document.getElementById('table-of-contents')

toggleTocBtn.addEventListener('click', function() {
toc.classList.add('toc-show')
toc.addEventListener('click', function() {
toc.classList.remove('toc-show')
})
})
/*]]>*/-->