Luca's literate Emacs config
Table of Contents
- 1. Introduction
- 2. early-init.el and init.el
- 3. Package manager
- 4. Emacs
- 4.1. Sane defaults
- 4.2. custom variables
- 4.3. Font
- 4.4. Zoom
- 4.5. macOS
- 4.6. Garbage collector magic hack
- 4.7. helpful
- 4.8. eldoc
- 4.9. exec path from shell
- 4.10. no littering
- 4.11. server mode
- 4.12. Auto-pair parenthesis
- 4.13. Rename file
- 4.14. xref
- 4.15. Don't close windows on escape
- 4.16. emacs extra packages
- 5. Keybindings
- 5.1. general
- 5.2. evil
- 5.2.1. evil mode
- 5.2.2. evil-collection
- 5.2.3. eval operator
- 5.2.4. evil-goggles
- 5.2.5. evil-snipe
- 5.2.6. evil-nerd-commenter
- 5.2.7. evil-surround
- 5.2.8. evil-indent-plus
- 5.2.9. evil cleverparens: outer form text object
- 5.2.10. evil-iedit-state
- 5.2.11. evil-mc: multi cursor
- 5.2.12. Fix scroll error when centaur tabs is active
- 5.3. which-key
- 6. Org
- 6.1. org mode
- 6.2. org code blocks in monospace font
- 6.3. org agenda
- 6.4. org capture templates
- 6.5. cycle only one heading
- 6.6. async tangle
- 6.7. org reverse datetree
- 6.8. org-superstar
- 6.9. highlight todo
- 6.10. org babel
- 6.11. ob-async
- 6.12. org-tree-slide
- 6.13. evil-org-mode
- 6.14. exporters org_export
- 6.15. org-appear
- 6.16. automatic latex preview
- 6.17. org-encrypt
- 6.18. use org-id in links
- 6.19. org-jupyter-mode prog_jupyter
- 6.20. org-roam org_roam
- 7. UI
- 7.1. all the icons
- 7.2. all the icons completion
- 7.3. doom modeline
- 7.4. Modus themes + alternate light/dark themes modus_themes
- 7.5. centaur tabs
- 7.6. dashboard dashboard
- 7.7. popup management
- 7.8. centered cursor mode ui_extra
- 7.9. hide mode line ui_extra
- 7.10. winum ui_extra
- 7.11. transpose frame ui_extra
- 7.12. Fill column indicator
- 7.13. Highlight indentation guides
- 7.14. Enlarge window
- 7.15. 8 colors theme
- 8. Completion framework
- 9. Programming
- 9.1. project
- 9.2. dired and friends
- 9.3. persistent scratch
- 9.4. rainbow parenthesis
- 9.5. restart-emacs
- 9.6. term
- 9.7. tramp
- 9.8. undo fu
- 9.9. git git
- 9.10. emacs tree-sitter prog_tree_sitter
- 9.11. envrc direnv
- 9.12. snippets yasnippet
- 9.13. vterm and friends prog_vterm
- 9.14. search google prog_extra
- 9.15. search github prog_extra
- 9.16. transient help commands prog_extra
- 9.17. transient increase/decrease font size prog_extra
- 9.18. isearch-mb prog_extra
- 9.19. olivetti mode extra_focus
- 9.20. darkroom extra_focus
- 10. Programming languages
- 11. Gimmicks
- 12. Provide modules
- 12.1. init-core
- 12.2. init-ui-extras
- 12.3. init-org-export
- 12.4. init-prog-tree-sitter
- 12.5. init-prog-nix
- 12.6. init-prog-lsp
- 12.7. init-prog-python
- 12.8. init-prog-jupyter
- 12.9. init-org-roam
- 12.10. init-prog-elisp
- 12.11. init-prog-r
- 12.12. init-prog-clojure
- 12.13. init-prog-vterm
- 12.14. init-prog-markdown
- 12.15. init-extra-focus
- 12.16. init-extra-web
- 12.17. init-extra-rss
- 12.18. init-extras
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:
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
anduse-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 configureevil
, 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:
- general
- eval operator
- evil-indent-plus
- evil-iedit-state
- evil-mc
- org mode
- ob-jupyter
- org-tree-slide
- evil-org-mode
- use org-id in links
- org-html-themify
- org-jupyter-mode
- Modus themes
- dashboard
- popup management
- projectile
- hydra-smerge
- envrc
- yasnippet
- dired
- web browser
- lsp mode
- dap-mode
- pytest
- jupyter
- evil-lisp state
- Adjust window size (transient)
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 multiplesetq
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
- If you use
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 "luca.cambiaghi@me.com") (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, usen
/N
to go to next/prev- Use the
gn
noun to, for example, change next match withcgn
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!! "
- Macros are saved in registers so you can simply
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 pointgrad
evals the top-level form (e.g. use-package blocks or functions)grak
evals the function inpython
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 toSPC o A
- Yo insert a src block use
, i
and then type initials e.g.jp
forjupyter-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 fororg-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
(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 linegqq
to fill the line and move to the end of itgwp
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 aconsult-line
) withC-l E
- You can run
embark-export-snapshot
withC-l S
- You can run
(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 pressM-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, useS-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 apyproject.toml
. In those cases, we need to create a.projectile
file in the root of the subprojects.projectile
excludes git ignored files fromprojectile-find-file
. Uselc/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 withSPC 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, usedired-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 withu
- 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 them as subtree with
- Open files with macos with
O
- View files with
go
and exit withq
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 setControlMaster 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
runautoreconf -i
- In
straight/build/zmq
runmake
emacs-zmq
installation:
- In
straight/build/emacs-zmq
runwget 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 havejupyter
on ourPATH
.- We assume
jupyter
is always installed in a virtual env associated with an.envrc
file - We load jupyter when we activate
envrc-mode
ifjupyter
is available
- We assume
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
withSPC l .
- Navigate symbols with
j
andk
- Navigate forms with
h
andl
- 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
withx
(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&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