mirror of
https://github.com/SqrtMinusOne/dotfiles.git
synced 2025-12-11 03:33:03 +03:00
4472 lines
136 KiB
Org Mode
4472 lines
136 KiB
Org Mode
#+PROPERTY: header-args :mkdirp yes
|
|
#+PROPERTY: header-args:bash :tangle-mode (identity #o755) :comments link :shebang "#!/usr/bin/env bash"
|
|
#+PROPERTY: header-args:emacs-lisp :tangle ~/.emacs.d/init.el :mkdirp yes
|
|
#+TODO: CHECK(s) | OFF(o)
|
|
|
|
#+begin_quote
|
|
One day we won't hate one another, no young boy will march to war and I will clean up my Emacs config. But that day isn't today.
|
|
#+end_quote
|
|
|
|
My [[https://www.gnu.org/software/emacs/][Emacs]] configuration.
|
|
|
|
As with other files in the repo, parts prefixed with (OFF) are not used but kept for historic purposes.
|
|
|
|
* Contents
|
|
:PROPERTIES:
|
|
:TOC: :include all :depth 4
|
|
:END:
|
|
:CONTENTS:
|
|
- [[#contents][Contents]]
|
|
- [[#primary-setup][Primary setup]]
|
|
- [[#measure-startup-speed][Measure startup speed]]
|
|
- [[#straightel][straight.el]]
|
|
- [[#use-package][use-package]]
|
|
- [[#performance][Performance]]
|
|
- [[#garbage-collection][Garbage collection]]
|
|
- [[#run-garbage-collection-when-emacs-is-unfocused][Run garbage collection when Emacs is unfocused]]
|
|
- [[#misc][Misc]]
|
|
- [[#native-compilation][Native compilation]]
|
|
- [[#anaconda--environment][Anaconda & environment]]
|
|
- [[#custom-file-location][Custom file location]]
|
|
- [[#no-littering][No littering]]
|
|
- [[#global-editing-configuration][Global editing configuration]]
|
|
- [[#general-keybindings-stuff][General keybindings stuff]]
|
|
- [[#generalel][general.el]]
|
|
- [[#which-key][which-key]]
|
|
- [[#evil-mode][Evil mode]]
|
|
- [[#evil][evil]]
|
|
- [[#addons][Addons]]
|
|
- [[#evil-collection][evil-collection]]
|
|
- [[#more-keybindigs][More keybindigs]]
|
|
- [[#escape-key][Escape key]]
|
|
- [[#home--end][Home & end]]
|
|
- [[#my-leader][My leader]]
|
|
- [[#universal-argument][Universal argument]]
|
|
- [[#profiler][Profiler]]
|
|
- [[#buffer-switching][Buffer switching]]
|
|
- [[#xref][xref]]
|
|
- [[#folding][Folding]]
|
|
- [[#zoom][Zoom]]
|
|
- [[#editing-helpers][Editing helpers]]
|
|
- [[#visual-fill-column-mode][Visual fill column mode]]
|
|
- [[#smartparens][smartparens]]
|
|
- [[#aggressive-indent][Aggressive Indent]]
|
|
- [[#delete-trailing-whitespace][Delete trailing whitespace]]
|
|
- [[#expand-region][Expand region]]
|
|
- [[#various-settings][Various settings]]
|
|
- [[#tabs][Tabs]]
|
|
- [[#scrolling-config][Scrolling config]]
|
|
- [[#clipboard][Clipboard]]
|
|
- [[#backups][Backups]]
|
|
- [[#undo-tree][Undo Tree]]
|
|
- [[#help][Help]]
|
|
- [[#ivy-counsel-swiper][Ivy, counsel, swiper]]
|
|
- [[#ivy-rich][ivy-rich]]
|
|
- [[#prescient][prescient]]
|
|
- [[#keybindings][Keybindings]]
|
|
- [[#off-helm][(OFF) Helm]]
|
|
- [[#treemacs][Treemacs]]
|
|
- [[#helper-functions][Helper functions]]
|
|
- [[#projectile][Projectile]]
|
|
- [[#company][Company]]
|
|
- [[#git--magit][Git & Magit]]
|
|
- [[#editorconfig][Editorconfig]]
|
|
- [[#off-avy][(OFF) Avy]]
|
|
- [[#snippets][Snippets]]
|
|
- [[#time-trackers][Time trackers]]
|
|
- [[#wakatime][WakaTime]]
|
|
- [[#activitywatch][ActivityWatch]]
|
|
- [[#ui][UI]]
|
|
- [[#general-ui--gui-settings][General UI & GUI Settings]]
|
|
- [[#theme--global-stuff][Theme & global stuff]]
|
|
- [[#custom-theme][Custom theme]]
|
|
- [[#font][Font]]
|
|
- [[#custom-frame-title][Custom frame title]]
|
|
- [[#tab-bar][Tab bar]]
|
|
- [[#setup][Setup]]
|
|
- [[#my-title][My title]]
|
|
- [[#modeline][Modeline]]
|
|
- [[#font-stuff][Font stuff]]
|
|
- [[#emojis][Emojis]]
|
|
- [[#ligatures][Ligatures]]
|
|
- [[#icons][Icons]]
|
|
- [[#highlight-todo][Highlight todo]]
|
|
- [[#text-highlight-improvements][Text highlight improvements]]
|
|
- [[#dired][Dired]]
|
|
- [[#basic-config--keybindings][Basic config & keybindings]]
|
|
- [[#addons][Addons]]
|
|
- [[#dired-on-emacs-28][dired+ on Emacs 28]]
|
|
- [[#tramp][TRAMP]]
|
|
- [[#bookmarks][Bookmarks]]
|
|
- [[#shells][Shells]]
|
|
- [[#vterm][vterm]]
|
|
- [[#configuration][Configuration]]
|
|
- [[#subterminal][Subterminal]]
|
|
- [[#eshell][Eshell]]
|
|
- [[#org-mode][Org Mode]]
|
|
- [[#installation--basic-settings][Installation & basic settings]]
|
|
- [[#encryption][Encryption]]
|
|
- [[#org-contrib][org-contrib]]
|
|
- [[#integration-with-evil][Integration with evil]]
|
|
- [[#literate-programing][Literate programing]]
|
|
- [[#python--jupyter][Python & Jupyter]]
|
|
- [[#hy][Hy]]
|
|
- [[#view-html-in-browser][View HTML in browser]]
|
|
- [[#setup][Setup]]
|
|
- [[#managing-jupyter-kernels][Managing Jupyter kernels]]
|
|
- [[#do-not-wrap-output-in-emacs-jupyter][Do not wrap output in emacs-jupyter]]
|
|
- [[#wrap-source-code-output][Wrap source code output]]
|
|
- [[#productivity--knowledge-management][Productivity & Knowledge management]]
|
|
- [[#capture-templates--various-settings][Capture templates & various settings]]
|
|
- [[#custom-agendas][Custom agendas]]
|
|
- [[#org-journal][Org Journal]]
|
|
- [[#org-roam][Org Roam]]
|
|
- [[#org-roam-protocol][org-roam-protocol]]
|
|
- [[#org-ref][org-ref]]
|
|
- [[#org-roam-bibtex][org-roam-bibtex]]
|
|
- [[#autocommit][autocommit]]
|
|
- [[#ui][UI]]
|
|
- [[#off-instant-equations-preview][(OFF) Instant equations preview]]
|
|
- [[#latex-fragments][LaTeX fragments]]
|
|
- [[#better-headers][Better headers]]
|
|
- [[#org-agenda-icons][Org Agenda Icons]]
|
|
- [[#export][Export]]
|
|
- [[#hugo][Hugo]]
|
|
- [[#jupyter-notebook][Jupyter Notebook]]
|
|
- [[#html-export][Html export]]
|
|
- [[#latex][LaTeX]]
|
|
- [[#keybindings--stuff][Keybindings & stuff]]
|
|
- [[#copy-a-link][Copy a link]]
|
|
- [[#presentations][Presentations]]
|
|
- [[#toc][TOC]]
|
|
- [[#system-configuration][System configuration]]
|
|
- [[#tables-for-guix-dependencies][Tables for Guix Dependencies]]
|
|
- [[#noweb-evaluations][Noweb evaluations]]
|
|
- [[#yadm-hook][yadm hook]]
|
|
- [[#off-eaf][(OFF) EAF]]
|
|
- [[#installation][Installation]]
|
|
- [[#config][Config]]
|
|
- [[#programming][Programming]]
|
|
- [[#general-setup][General setup]]
|
|
- [[#lsp][LSP]]
|
|
- [[#setup][Setup]]
|
|
- [[#integrations][Integrations]]
|
|
- [[#keybindings][Keybindings]]
|
|
- [[#flycheck][Flycheck]]
|
|
- [[#tree-sitter][Tree Sitter]]
|
|
- [[#off-dap][(OFF) DAP]]
|
|
- [[#off-tabnine][(OFF) TabNine]]
|
|
- [[#off-code-compass][(OFF) Code Compass]]
|
|
- [[#dependencies][Dependencies]]
|
|
- [[#plugin][Plugin]]
|
|
- [[#off-format-all][(OFF) Format-all]]
|
|
- [[#general-additional-config][General additional config]]
|
|
- [[#web-development][Web development]]
|
|
- [[#emmet][Emmet]]
|
|
- [[#prettier][Prettier]]
|
|
- [[#typescript][TypeScript]]
|
|
- [[#javascript][JavaScript]]
|
|
- [[#jest][Jest]]
|
|
- [[#vuejs][Vue.js]]
|
|
- [[#mmm-mode-fix][mmm-mode fix]]
|
|
- [[#svelte][Svelte]]
|
|
- [[#scss][SCSS]]
|
|
- [[#php][PHP]]
|
|
- [[#latex][LaTeX]]
|
|
- [[#auctex][AUCTeX]]
|
|
- [[#bibtex][BibTeX]]
|
|
- [[#import-sty][Import *.sty]]
|
|
- [[#snippets][Snippets]]
|
|
- [[#greek-letters][Greek letters]]
|
|
- [[#english-letters][English letters]]
|
|
- [[#math-symbols][Math symbols]]
|
|
- [[#section-snippets][Section snippets]]
|
|
- [[#other-markup-languages][Other markup languages]]
|
|
- [[#markdown][Markdown]]
|
|
- [[#plantuml][PlantUML]]
|
|
- [[#languagetool][LanguageTool]]
|
|
- [[#lisp][Lisp]]
|
|
- [[#meta-lisp][Meta Lisp]]
|
|
- [[#emacs-lisp][Emacs Lisp]]
|
|
- [[#common-lisp][Common lisp]]
|
|
- [[#clojure][Clojure]]
|
|
- [[#hy][Hy]]
|
|
- [[#scheme][Scheme]]
|
|
- [[#clips][CLIPS]]
|
|
- [[#python][Python]]
|
|
- [[#pipenv][pipenv]]
|
|
- [[#yapf][yapf]]
|
|
- [[#isort][isort]]
|
|
- [[#sphinx-doc][sphinx-doc]]
|
|
- [[#pytest][pytest]]
|
|
- [[#fix-comint-buffer-width][Fix comint buffer width]]
|
|
- [[#code-cells][code-cells]]
|
|
- [[#tensorboard][tensorboard]]
|
|
- [[#java][Java]]
|
|
- [[#go][Go]]
|
|
- [[#net][.NET]]
|
|
- [[#c][C#]]
|
|
- [[#msbuild][MSBuild]]
|
|
- [[#fish][fish]]
|
|
- [[#sh][sh]]
|
|
- [[#haskell][Haskell]]
|
|
- [[#json][JSON]]
|
|
- [[#yaml][YAML]]
|
|
- [[#env][.env]]
|
|
- [[#csv][CSV]]
|
|
- [[#off-pdf][(OFF) PDF]]
|
|
- [[#docker][Docker]]
|
|
- [[#apps--misc][Apps & Misc]]
|
|
- [[#managing-dotfiles][Managing dotfiles]]
|
|
- [[#open-emacs-config][Open Emacs config]]
|
|
- [[#open-magit-for-yadm][Open Magit for yadm]]
|
|
- [[#open-a-dotfile][Open a dotfile]]
|
|
- [[#notmuch][Notmuch]]
|
|
- [[#elfeed][Elfeed]]
|
|
- [[#some-additions][Some additions]]
|
|
- [[#youtube][YouTube]]
|
|
- [[#man--tldr][man & tldr]]
|
|
- [[#erc][ERC]]
|
|
- [[#docker][Docker]]
|
|
- [[#progidy][Progidy]]
|
|
- [[#google-translate][Google Translate]]
|
|
- [[#pomidor][Pomidor]]
|
|
- [[#eww][EWW]]
|
|
- [[#proced][proced]]
|
|
- [[#screenshotel][screenshot.el]]
|
|
- [[#snow][Snow]]
|
|
- [[#zone][Zone]]
|
|
- [[#discord-integration][Discord integration]]
|
|
- [[#guix][Guix]]
|
|
- [[#guix-settings][Guix settings]]
|
|
:END:
|
|
* Primary setup
|
|
** Measure startup speed
|
|
A small function to print out the loading time and number of GCs during the loading. Can be useful as a point of data for optimizing Emacs startup time.
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'emacs-startup-hook
|
|
(lambda ()
|
|
(message "*** Emacs loaded in %s with %d garbage collections."
|
|
(format "%.2f seconds"
|
|
(float-time
|
|
(time-subtract after-init-time before-init-time)))
|
|
gcs-done)))
|
|
|
|
;; (setq use-package-verbose t)
|
|
#+end_src
|
|
** straight.el
|
|
Straight.el is my Emacs package manager of choice. Its advantages & disadvantages over other options are listed pretty thoroughly in the README file in the repo.
|
|
|
|
The following is a straight.el bootstrap script.
|
|
|
|
References:
|
|
- [[https://github.com/raxod502/straight.el][straight.el repo]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(defvar bootstrap-version)
|
|
(let ((bootstrap-file
|
|
(expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
|
|
(bootstrap-version 5))
|
|
(unless (file-exists-p bootstrap-file)
|
|
(with-current-buffer
|
|
(url-retrieve-synchronously
|
|
"https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
|
|
'silent 'inhibit-cookies)
|
|
(goto-char (point-max))
|
|
(eval-print-last-sexp)))
|
|
(load bootstrap-file nil 'nomessage))
|
|
#+end_src
|
|
** use-package
|
|
A macro to simplify package specification & configuration. Integrates with straight.el.
|
|
|
|
Set ~use-package-verbose~ to ~t~ to print out individual package loading time.
|
|
|
|
References:
|
|
- [[https://github.com/jwiegley/use-package][use-package repo]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(straight-use-package 'use-package)
|
|
(eval-when-compile (require 'use-package))
|
|
;; (setq use-package-verbose t)
|
|
#+end_src
|
|
** Performance
|
|
*** Garbage collection
|
|
Just setting ~gc-cons-treshold~ to a larger value.
|
|
|
|
| Note | Type |
|
|
|-------+---------------------------------------------------|
|
|
| CHECK | The value may be too large for an interactive use |
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq gc-cons-threshold 80000000)
|
|
(setq read-process-output-max (* 1024 1024))
|
|
#+end_src
|
|
*** Run garbage collection when Emacs is unfocused
|
|
Run GC when Emacs loses focus. Time will tell if that's a good idea.
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'emacs-startup-hook
|
|
(lambda ()
|
|
(if (boundp 'after-focus-change-function)
|
|
(add-function :after after-focus-change-function
|
|
(lambda ()
|
|
(unless (frame-focus-state)
|
|
(garbage-collect))))
|
|
(add-hook 'after-focus-change-function 'garbage-collect))))
|
|
#+end_src
|
|
*** Misc
|
|
The following variable is true when my machine is not powerful enough for some resource-heavy packages.
|
|
#+begin_src emacs-lisp
|
|
(setq my/lowpower (string= (system-name) "azure"))
|
|
#+end_src
|
|
|
|
And the following is true if Emacs is meant to be used with TRAMP over slow ssh
|
|
#+begin_src emacs-lisp
|
|
(setq my/slow-ssh (string= (getenv "IS_TRAMP") "true"))
|
|
#+end_src
|
|
*** Native compilation
|
|
Set number of jobs to 1 on low-power machines
|
|
#+begin_src emacs-lisp
|
|
(when my/lowpower
|
|
(setq comp-async-jobs-number 1))
|
|
#+end_src
|
|
** Anaconda & environment
|
|
[[https://www.anaconda.com/][Anaconda]] is a free package and environment manager. I currently use it to manage multiple versions of Python and Node.js
|
|
|
|
The following code uses the conda package to activate the base environment on startup if Emacs is launched outside the environment.
|
|
|
|
Also, there are some strange things happening if vterm is launched with conda activated from Emacs, so I advice =conda-env-activate= to set an auxillary environment variable.
|
|
|
|
References:
|
|
- [[https://docs.anaconda.com/][Anaconda docs]]
|
|
- [[https://github.com/necaris/conda.el][conda.el repo]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package conda
|
|
:straight t
|
|
:if (executable-find "conda")
|
|
:config
|
|
(setq conda-anaconda-home (string-replace "/bin/conda" "" (executable-find "conda")))
|
|
(setq conda-env-home-directory (expand-file-name "~/.conda/"))
|
|
(setq conda-env-subdirectory "envs")
|
|
(setenv "INIT_CONDA" "true")
|
|
(advice-add 'conda-env-activate :after
|
|
(lambda (&rest _) (setenv "EMACS_CONDA_ENV" conda-env-current-name)))
|
|
(unless (getenv "CONDA_DEFAULT_ENV")
|
|
(conda-env-activate "general")))
|
|
#+end_src
|
|
|
|
Also, I sometimes need to know if a program is running inside Emacs (say, inside a terminal emulator). To do that, I set the following environment variable:
|
|
#+begin_src emacs-lisp
|
|
(setenv "IS_EMACS" "true")
|
|
#+end_src
|
|
** Custom file location
|
|
By default, custom writes stuff to =init.el=, which is somewhat annoying. The following makes a separate file =custom.el=
|
|
#+begin_src emacs-lisp
|
|
(setq custom-file (concat user-emacs-directory "custom.el"))
|
|
(load custom-file 'noerror)
|
|
#+end_src
|
|
** No littering
|
|
#+begin_src emacs-lisp
|
|
(use-package no-littering
|
|
:straight t)
|
|
#+end_src
|
|
* Global editing configuration
|
|
** General keybindings stuff
|
|
*** general.el
|
|
general.el provides a convenient interface to manage Emacs keybindings.
|
|
|
|
References:
|
|
- [[https://github.com/noctuid/general.el][general.el repo]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package general
|
|
:straight t
|
|
:config
|
|
(general-evil-setup))
|
|
#+end_src
|
|
*** which-key
|
|
A package that displays the available keybindings in a popup.
|
|
|
|
Pretty useful, as Emacs seems to have more keybindings than I can remember at any given point.
|
|
|
|
References:
|
|
- [[https://github.com/justbur/emacs-which-key][which-key repo]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package which-key
|
|
:config
|
|
(setq which-key-idle-delay (if my/lowpower 1 0.3))
|
|
(setq which-key-popup-type 'frame)
|
|
(which-key-mode)
|
|
(which-key-setup-side-window-bottom)
|
|
(set-face-attribute 'which-key-local-map-description-face nil
|
|
:weight 'bold)
|
|
:straight t)
|
|
#+end_src
|
|
** Evil mode
|
|
A whole ecosystem of packages that emulates the main features of Vim. Probably the best vim emulator out there.
|
|
|
|
The only problem is that the package name makes it hard to google anything by just typing "evil".
|
|
|
|
References:
|
|
- [[https://github.com/emacs-evil/evil][evil repo]]
|
|
- [[https://www.youtube.com/watch?v=JWD1Fpdd4Pc][(YouTube) Evil Mode: Or, How I Learned to Stop Worrying and Love Emacs]]
|
|
*** evil
|
|
Basic evil configuration.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package evil
|
|
:straight t
|
|
:init
|
|
(setq evil-want-integration t)
|
|
(setq evil-want-C-u-scroll t)
|
|
(setq evil-want-keybinding nil)
|
|
:config
|
|
(evil-mode 1)
|
|
(setq evil-search-module 'evil-search)
|
|
(setq evil-split-window-below t)
|
|
(setq evil-vsplit-window-right t)
|
|
;; (setq evil-respect-visual-line-mode t)
|
|
(evil-set-undo-system 'undo-tree)
|
|
;; (add-to-list 'evil-emacs-state-modes 'dired-mode)
|
|
)
|
|
#+end_src
|
|
*** Addons
|
|
[[https://github.com/emacs-evil/evil-surround][evil-surround]] emulates one of my favorite vim plugins, surround.vim. Adds a lot of parentheses management options.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package evil-surround
|
|
:straight t
|
|
:after evil
|
|
:config
|
|
(global-evil-surround-mode 1))
|
|
#+end_src
|
|
|
|
[[https://github.com/linktohack/evil-commentary][evil-commentary]] emulates commentary.vim.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package evil-commentary
|
|
:straight t
|
|
:after evil
|
|
:config
|
|
(evil-commentary-mode))
|
|
#+end_src
|
|
|
|
[[https://github.com/blorbx/evil-quickscope][evil-quickscope]] emulates quickscope.vim. It highlights the important target characters for f, F, t, T keys.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package evil-quickscope
|
|
:straight t
|
|
:after evil
|
|
:config
|
|
:hook ((prog-mode . turn-on-evil-quickscope-mode)
|
|
(LaTeX-mode . turn-on-evil-quickscope-mode)
|
|
(org-mode . turn-on-evil-quickscope-mode)))
|
|
#+end_src
|
|
|
|
[[https://github.com/cofi/evil-numbers][evil-numbers]] allows incrementing and decrementing numbers at point.
|
|
#+begin_src emacs-lisp
|
|
(use-package evil-numbers
|
|
:straight t
|
|
:commands (evil-numbers/inc-at-pt evil-numbers/dec-at-pt)
|
|
:init
|
|
(general-nmap
|
|
"g+" 'evil-numbers/inc-at-pt
|
|
"g-" 'evil-numbers/dec-at-pt))
|
|
#+end_src
|
|
|
|
[[https://github.com/edkolev/evil-lion][evil-lion]] provides alignment operators.
|
|
#+begin_src emacs-lisp
|
|
(use-package evil-lion
|
|
:straight t
|
|
:config
|
|
(setq evil-lion-left-align-key (kbd "g a"))
|
|
(setq evil-lion-right-align-key (kbd "g A"))
|
|
(evil-lion-mode))
|
|
#+end_src
|
|
*** evil-collection
|
|
[[https://github.com/emacs-evil/evil-collection][evil-collection]] is a package that provides evil bindings for a lot of different packages. One can see the whole list in the [[https://github.com/emacs-evil/evil-collection/tree/master/modes][modes]] folder.
|
|
|
|
I don't enable the entire package, just the modes I need.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package evil-collection
|
|
:straight t
|
|
:after evil
|
|
:config
|
|
(evil-collection-init
|
|
'(eww
|
|
proced
|
|
dired
|
|
debug
|
|
guix
|
|
calc
|
|
docker
|
|
geiser
|
|
pdf
|
|
info
|
|
elfeed
|
|
edebug
|
|
bookmark
|
|
company
|
|
vterm
|
|
flycheck
|
|
profiler
|
|
cider
|
|
explain-pause-mode
|
|
notmuch
|
|
custom
|
|
xref
|
|
eshell
|
|
helpful
|
|
compile
|
|
comint
|
|
magit
|
|
prodigy)))
|
|
#+end_src
|
|
** More keybindigs
|
|
The main keybindigs setup is positioned after evil mode to take the latter into account.
|
|
*** Escape key
|
|
Use escape key instead of =C-g= whenever possible.
|
|
|
|
I must have copied it from somewhere, but as I googled to find out the original source, I discovered quite a number of variations of the following code over time.
|
|
|
|
I wonder if Richard Dawkins was inspired by something like this a few decades ago.
|
|
#+begin_src emacs-lisp
|
|
(defun minibuffer-keyboard-quit ()
|
|
"Abort recursive edit.
|
|
In Delete Selection mode, if the mark is active, just deactivate it;
|
|
then it takes a second \\[keyboard-quit] to abort the minibuffer."
|
|
(interactive)
|
|
(if (and delete-selection-mode transient-mark-mode mark-active)
|
|
(setq deactivate-mark t)
|
|
(when (get-buffer "*Completions*") (delete-windows-on "*Completions*"))
|
|
(abort-recursive-edit)))
|
|
|
|
(general-define-key
|
|
:keymaps '(normal visual global)
|
|
[escape] 'keyboard-quit)
|
|
|
|
(general-define-key
|
|
:keymaps '(minibuffer-local-map
|
|
minibuffer-local-ns-map
|
|
minibuffer-local-completion-map
|
|
minibuffer-local-must-match-map
|
|
minibuffer-local-isearch-map)
|
|
[escape] 'minibuffer-keyboard-quit)
|
|
#+end_src
|
|
*** Home & end
|
|
#+begin_src emacs-lisp
|
|
(general-def :states '(normal insert visual)
|
|
"<home>" 'beginning-of-line
|
|
"<end>" 'end-of-line)
|
|
#+end_src
|
|
*** My leader
|
|
Using the =SPC= key as a sort of leader key.
|
|
|
|
#+begin_src emacs-lisp
|
|
(general-create-definer my-leader-def
|
|
:keymaps 'override
|
|
:prefix "SPC"
|
|
:states '(normal motion emacs))
|
|
|
|
|
|
(general-def :states '(normal motion emacs) "SPC" nil)
|
|
|
|
(my-leader-def "?" 'which-key-show-top-level)
|
|
(my-leader-def "E" 'eval-expression)
|
|
#+end_src
|
|
*** Universal argument
|
|
Change the universal argument to =M-u=
|
|
#+begin_src emacs-lisp
|
|
(general-def
|
|
:keymaps 'universal-argument-map
|
|
"M-u" 'universal-argument-more)
|
|
(general-def
|
|
:keymaps 'override
|
|
:states '(normal motion emacs insert visual)
|
|
"M-u" 'universal-argument)
|
|
#+end_src
|
|
*** Profiler
|
|
The built-in profiler is a magnificent tool to troubleshoot performance issues.
|
|
#+begin_src emacs-lisp
|
|
(my-leader-def "Ps" 'profiler-start)
|
|
(my-leader-def "Pe" 'profiler-stop)
|
|
(my-leader-def "Pp" 'profiler-report)
|
|
#+end_src
|
|
*** Buffer switching
|
|
Some keybindings I used in vim to switch buffers and can't let go of.
|
|
#+begin_src emacs-lisp
|
|
(general-define-key
|
|
:keymaps 'override
|
|
"C-<right>" 'evil-window-right
|
|
"C-<left>" 'evil-window-left
|
|
"C-<up>" 'evil-window-up
|
|
"C-<down>" 'evil-window-down
|
|
"C-h" 'evil-window-left
|
|
"C-l" 'evil-window-right
|
|
"C-k" 'evil-window-up
|
|
"C-j" 'evil-window-down
|
|
"C-x h" 'previous-buffer
|
|
"C-x l" 'next-buffer)
|
|
#+end_src
|
|
|
|
And winner-mode to keep the history of window states.
|
|
#+begin_src emacs-lisp
|
|
(winner-mode 1)
|
|
(define-key evil-window-map (kbd "u") 'winner-undo)
|
|
(define-key evil-window-map (kbd "U") 'winner-redo)
|
|
#+end_src
|
|
*** xref
|
|
Some keybindings for xref, Emacs' built-in systems for managing identifiers.
|
|
#+begin_src emacs-lisp
|
|
(general-nmap
|
|
"gD" 'xref-find-definitions-other-window
|
|
"gr" 'xref-find-references)
|
|
|
|
(my-leader-def
|
|
"fx" 'xref-find-apropos)
|
|
#+end_src
|
|
*** Folding
|
|
#+begin_src emacs-lisp
|
|
(general-nmap :keymaps '(hs-minor-mode-map outline-minor-mode-map)
|
|
"ze" 'hs-hide-level
|
|
"TAB" 'evil-toggle-fold)
|
|
#+end_src
|
|
*** Zoom
|
|
#+begin_src emacs-lisp
|
|
(defun my/zoom-in ()
|
|
"Increase font size by 10 points"
|
|
(interactive)
|
|
(set-face-attribute 'default nil
|
|
:height
|
|
(+ (face-attribute 'default :height)
|
|
10)))
|
|
|
|
(defun my/zoom-out ()
|
|
"Decrease font size by 10 points"
|
|
(interactive)
|
|
(set-face-attribute 'default nil
|
|
:height
|
|
(- (face-attribute 'default :height)
|
|
10)))
|
|
|
|
;; change font size, interactively
|
|
(global-set-key (kbd "C-+") 'my/zoom-in)
|
|
(global-set-key (kbd "C-=") 'my/zoom-out)
|
|
#+end_src
|
|
** Editing helpers
|
|
*** Visual fill column mode
|
|
#+begin_src emacs-lisp
|
|
(use-package visual-fill-column
|
|
:straight t
|
|
:config
|
|
(add-hook 'visual-fill-column-mode-hook
|
|
(lambda () (setq visual-fill-column-center-text t))))
|
|
#+end_src
|
|
*** smartparens
|
|
A minor mode to deal with pairs. Its functionality overlaps with evil-surround, but smartparens provides the most comfortable way to do stuff like automatically insert pairs.
|
|
|
|
References:
|
|
- [[https://github.com/Fuco1/smartparens][smartparens repo]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package smartparens
|
|
:straight t)
|
|
#+end_src
|
|
*** Aggressive Indent
|
|
A package to keep the code intended.
|
|
|
|
Doesn't work too well with js ecosystem, because the lsp-based indentation is rather slow, but nice for Lisps.
|
|
|
|
References:
|
|
- [[https://github.com/Malabarba/aggressive-indent-mode][aggressive-indent-mode repo]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package aggressive-indent
|
|
:commands (aggressive-indent-mode)
|
|
:straight t)
|
|
#+end_src
|
|
*** Delete trailing whitespace
|
|
Delete trailing whitespace on save, unless in particular modes where trailing whitespace is important, like Markdown.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq my/trailing-whitespace-modes '(markdown-mode))
|
|
|
|
(require 'cl-extra)
|
|
|
|
(add-hook 'before-save-hook
|
|
(lambda ()
|
|
(unless (cl-some #'derived-mode-p my/trailing-whitespace-modes)
|
|
(delete-trailing-whitespace))))
|
|
#+end_src
|
|
*** Expand region
|
|
#+begin_src emacs-lisp
|
|
(use-package expand-region
|
|
:straight t
|
|
:commands (er/expand-region)
|
|
:init
|
|
(general-nmap "+" 'er/expand-region))
|
|
#+end_src
|
|
** Various settings
|
|
*** Tabs
|
|
Some default settings to manage tabs.
|
|
#+begin_src emacs-lisp
|
|
(setq tab-always-indent nil)
|
|
|
|
(setq-default default-tab-width 4)
|
|
(setq-default tab-width 4)
|
|
(setq-default evil-indent-convert-tabs nil)
|
|
(setq-default indent-tabs-mode nil)
|
|
(setq-default tab-width 4)
|
|
(setq-default evil-shift-round nil)
|
|
#+end_src
|
|
*** Scrolling config
|
|
#+begin_src emacs-lisp
|
|
(setq scroll-conservatively scroll-margin)
|
|
(setq scroll-step 1)
|
|
(setq scroll-preserve-screen-position t)
|
|
(setq scroll-error-top-bottom t)
|
|
(setq mouse-wheel-progressive-speed nil)
|
|
(setq mouse-wheel-inhibit-click-time nil)
|
|
#+end_src
|
|
*** Clipboard
|
|
#+begin_src emacs-lisp
|
|
(setq select-enable-clipboard t)
|
|
(setq mouse-yank-at-point t)
|
|
#+end_src
|
|
*** Backups
|
|
#+begin_src emacs-lisp
|
|
(setq backup-inhibited t)
|
|
(setq auto-save-default nil)
|
|
#+end_src
|
|
** Undo Tree
|
|
Replaces Emacs build-in sequential undo system with a tree-based one. Probably one of the greatest features of Emacs as a text editor.
|
|
|
|
References:
|
|
- [[https://www.emacswiki.org/emacs/UndoTree][UndoTree on EmacsWiki]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package undo-tree
|
|
:straight t
|
|
:config
|
|
(global-undo-tree-mode)
|
|
(setq undo-tree-visualizer-diff t)
|
|
(setq undo-tree-visualizer-timestamps t)
|
|
|
|
(my-leader-def "u" 'undo-tree-visualize)
|
|
(fset 'undo-auto-amalgamate 'ignore)
|
|
(setq undo-limit 6710886400)
|
|
(setq undo-strong-limit 100663296)
|
|
(setq undo-outer-limit 1006632960))
|
|
#+end_src
|
|
|
|
** Help
|
|
[[https://github.com/Wilfred/helpful][helpful]] package improves the =*help*= buffer.
|
|
#+begin_src emacs-lisp
|
|
(use-package helpful
|
|
:straight t
|
|
:commands (helpful-callable
|
|
helpful-variable
|
|
helpful-key
|
|
helpful-macro
|
|
helpful-function
|
|
helpful-command))
|
|
|
|
#+end_src
|
|
|
|
As I use =C-h= to switch buffers, I moved the help to =SPC-h= with the code below. Of course, I didn't type it all by hand.
|
|
#+begin_src emacs-lisp
|
|
(my-leader-def
|
|
:infix "h"
|
|
"RET" 'view-order-manuals
|
|
"." 'display-local-help
|
|
"?" 'help-for-help
|
|
"C" 'describe-coding-system
|
|
"F" 'Info-goto-emacs-command-node
|
|
"I" 'describe-input-method
|
|
"K" 'Info-goto-emacs-key-command-node
|
|
"L" 'describe-language-environment
|
|
"P" 'describe-package
|
|
"S" 'info-lookup-symbol
|
|
"a" 'helm-apropos
|
|
"b" 'describe-bindings
|
|
"c" 'describe-key-briefly
|
|
"d" 'apropos-documentation
|
|
"e" 'view-echo-area-messages
|
|
"f" 'helpful-function
|
|
"g" 'describe-gnu-project
|
|
"h" 'view-hello-file
|
|
"i" 'info
|
|
"k" 'helpful-key
|
|
"l" 'view-lossage
|
|
"m" 'describe-mode
|
|
"n" 'view-emacs-news
|
|
"o" 'describe-symbol
|
|
"p" 'finder-by-keyword
|
|
"q" 'help-quit
|
|
"r" 'info-emacs-manual
|
|
"s" 'describe-syntax
|
|
"t" 'help-with-tutorial
|
|
"v" 'helpful-variable
|
|
"w" 'where-is
|
|
"<f1>" 'help-for-help
|
|
"C-\\" 'describe-input-method
|
|
"C-a" 'about-emacs
|
|
"C-c" 'describe-copying
|
|
"C-d" 'view-emacs-debugging
|
|
"C-e" 'view-external-packages
|
|
"C-f" 'view-emacs-FAQ
|
|
"C-h" 'help-for-help
|
|
"C-n" 'view-emacs-news
|
|
"C-o" 'describe-distribution
|
|
"C-p" 'view-emacs-problems
|
|
"C-s" 'search-forward-help-for-help
|
|
"C-t" 'view-emacs-todo
|
|
"C-w" 'describe-no-warranty)
|
|
#+end_src
|
|
|
|
** Ivy, counsel, swiper
|
|
Minibuffer completion tools for Emacs.
|
|
|
|
References:
|
|
- [[https://oremacs.com/swiper/][repo]]
|
|
- [[https://oremacs.com/swiper/][User Manual]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package ivy
|
|
:straight t
|
|
:config
|
|
(setq ivy-use-virtual-buffers t)
|
|
(ivy-mode))
|
|
|
|
(use-package counsel
|
|
:straight t
|
|
:after ivy
|
|
:config
|
|
(counsel-mode))
|
|
|
|
(use-package swiper
|
|
:defer t
|
|
:straight t)
|
|
#+end_src
|
|
|
|
*** ivy-rich
|
|
[[https://github.com/Yevgnen/ivy-rich][ivy-rich]] provides more informative interface for ivy.
|
|
#+begin_src emacs-lisp
|
|
(use-package ivy-rich
|
|
:straight t
|
|
:after ivy
|
|
:config
|
|
(ivy-rich-mode 1)
|
|
(setcdr (assq t ivy-format-functions-alist) #'ivy-format-function-line))
|
|
#+end_src
|
|
|
|
*** prescient
|
|
A package which enhances sorting & filtering of candidates. =ivy-prescient= adds integration with Ivy.
|
|
|
|
References:
|
|
- [[https://github.com/raxod502/prescient.el][prescient.el repo]]
|
|
#+begin_src emacs-lisp :noweb yes
|
|
(use-package ivy-prescient
|
|
:straight t
|
|
:after counsel
|
|
:config
|
|
(ivy-prescient-mode +1)
|
|
(setq ivy-prescient-retain-classic-highlighting t)
|
|
(prescient-persist-mode 1)
|
|
(setq ivy-prescient-sort-commands
|
|
'(:not swiper
|
|
swiper-isearch
|
|
ivy-switch-buffer
|
|
;; ivy-resume
|
|
;; ivy--restore-session
|
|
lsp-ivy-workspace-symbol
|
|
counsel-grep
|
|
;; counsel-find-file
|
|
counsel-git-grep
|
|
counsel-rg
|
|
counsel-ag
|
|
counsel-ack
|
|
counsel-fzf
|
|
counsel-pt
|
|
counsel-imenu
|
|
counsel-yank-pop
|
|
counsel-recentf
|
|
counsel-buffer-or-recentf
|
|
proced-filter-interactive
|
|
proced-sort-interactive))
|
|
;; Do not use prescient in find-file
|
|
(ivy--alist-set 'ivy-sort-functions-alist #'read-file-name-internal #'ivy-sort-file-function-default))
|
|
#+end_src
|
|
*** Keybindings
|
|
#+begin_src emacs-lisp
|
|
(my-leader-def
|
|
:infix "f"
|
|
"b" 'counsel-switch-buffer
|
|
"e" 'conda-env-activate
|
|
"f" 'project-find-file
|
|
"c" 'counsel-yank-pop
|
|
"a" 'counsel-rg
|
|
"A" 'counsel-ag)
|
|
|
|
(general-imap
|
|
"C-y" 'counsel-yank-pop)
|
|
|
|
(my-leader-def "SPC" 'ivy-resume)
|
|
(my-leader-def "s" 'swiper-isearch
|
|
"S" 'swiper-all)
|
|
|
|
(general-define-key
|
|
:keymaps '(ivy-minibuffer-map swiper-map)
|
|
"M-j" 'ivy-next-line
|
|
"M-k" 'ivy-previous-line
|
|
"<C-return>" 'ivy-call
|
|
"M-RET" 'ivy-immediate-done
|
|
[escape] 'minibuffer-keyboard-quit)
|
|
#+end_src
|
|
** OFF (OFF) Helm
|
|
Config for the Helm incremental completion framework. I switched to Ivy some time ago, but keep the configuration just in case.
|
|
#+begin_src emacs-lisp :tangle no
|
|
(use-package helm
|
|
:init
|
|
(require 'helm-config)
|
|
(setq helm-split-window-in-side-p t)
|
|
(setq helm-move-to-line-cycle-in-source t)
|
|
:straight t
|
|
:config
|
|
(helm-mode 1)
|
|
(helm-autoresize-mode 1))
|
|
|
|
(use-package helm-ag
|
|
:straight t)
|
|
|
|
(use-package helm-rg
|
|
:straight t)
|
|
|
|
(general-nmap
|
|
:keymaps 'helm-ag-mode-map
|
|
"RET" 'helm-ag-mode-jump
|
|
"M-RET" 'helm-ag-mode-jump-other-window)
|
|
|
|
(general-nmap
|
|
:keymaps 'helm-occur-mode-map
|
|
"RET" 'helm-occur-mode-goto-line
|
|
"M-RET" 'helm-occur-mode-goto-line-ow)
|
|
|
|
(general-define-key "M-x" 'helm-M-x)
|
|
(my-leader-def
|
|
"fb" 'helm-buffers-list
|
|
"fs" 'helm-lsp-workspace-symbol
|
|
"fw" 'helm-lsp-global-workspace-symbol
|
|
"fc" 'helm-show-kill-ring
|
|
;; "fa" 'helm-do-ag-project-root
|
|
"fm" 'helm-bookmarks
|
|
"ff" 'project-find-file
|
|
"fe" 'conda-env-activate)
|
|
|
|
(my-leader-def "s" 'helm-occur)
|
|
(my-leader-def "SPC" 'helm-resume)
|
|
|
|
(general-define-key
|
|
:keymaps 'helm-map
|
|
"C-j" 'helm-next-line
|
|
"C-k" 'helm-previous-line)
|
|
|
|
(general-define-key
|
|
:keymaps '(helm-find-files-map helm-locate-map)
|
|
"C-h" 'helm-find-files-up-one-level
|
|
"C-l" 'helm-execute-persistent-action)
|
|
|
|
(general-imap
|
|
"C-y" 'helm-show-kill-ring)
|
|
;; (general-nmap "C-p" 'project-find-file)
|
|
#+end_src
|
|
** Treemacs
|
|
[[https://github.com/Alexander-Miller/treemacs][Treemacs]] calls itself a tree layout file explorer, but looks more like a project and workspace management system.
|
|
|
|
Integrates with evil, magit and projectile.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package treemacs
|
|
:straight t
|
|
:commands (treemacs treemacs-switch-workspace treemacs-edit-workspace)
|
|
:config
|
|
(setq treemacs-follow-mode nil)
|
|
(setq treemacs-follow-after-init nil)
|
|
(setq treemacs-space-between-root-nodes nil)
|
|
(treemacs-git-mode 'extended)
|
|
(with-eval-after-load 'treemacs
|
|
(add-to-list 'treemacs-pre-file-insert-predicates #'treemacs-is-file-git-ignored?)))
|
|
|
|
(use-package treemacs-evil
|
|
:after (treemacs evil)
|
|
:straight t)
|
|
|
|
(use-package treemacs-magit
|
|
:after (treemacs magit)
|
|
:straight t)
|
|
|
|
(general-define-key
|
|
:keymaps '(normal override global)
|
|
"C-n" 'treemacs)
|
|
|
|
(general-define-key
|
|
:keymaps '(treemacs-mode-map) [mouse-1] #'treemacs-single-click-expand-action)
|
|
|
|
(my-leader-def
|
|
"tw" 'treemacs-switch-workspace
|
|
"te" 'treemacs-edit-workspaces)
|
|
#+end_src
|
|
*** Helper functions
|
|
#+begin_src emacs-lisp
|
|
(defun my/treemacs-open-dired ()
|
|
"Open dired at given treemacs node"
|
|
(interactive)
|
|
(let (path (treemacs--prop-at-point :path))
|
|
(dired path)))
|
|
|
|
(defun my/treemacs-open-vterm ()
|
|
"Open vterm at given treemacs node"
|
|
(interactive)
|
|
(let ((default-directory (file-name-directory (treemacs--prop-at-point :path))))
|
|
(vterm)))
|
|
|
|
(with-eval-after-load 'treemacs
|
|
(general-define-key
|
|
:keymaps 'treemacs-mode-map
|
|
:states '(treemacs)
|
|
"gd" 'my/treemacs-open-dired
|
|
"gt" 'my/treemacs-open-vterm
|
|
"`" 'my/treemacs-open-vterm))
|
|
#+end_src
|
|
** Projectile
|
|
[[https://github.com/bbatsov/projectile][Projectile]] gives a bunch of useful functions for managing projects, like finding files within a project, fuzzy-find, replace, etc.
|
|
|
|
~defadvice~ is meant to speed projectile up with TRAMP a bit.
|
|
#+begin_src emacs-lisp
|
|
(use-package projectile
|
|
:straight t
|
|
:config
|
|
(projectile-mode +1)
|
|
(setq projectile-project-search-path '("~/Code" "~/Documents"))
|
|
(defadvice projectile-project-root (around ignore-remote first activate)
|
|
(unless (file-remote-p default-directory) ad-do-it)))
|
|
|
|
(use-package counsel-projectile
|
|
:after (counsel projectile)
|
|
:straight t)
|
|
|
|
(use-package treemacs-projectile
|
|
:after (treemacs projectile)
|
|
:straight t)
|
|
|
|
(my-leader-def
|
|
"p" 'projectile-command-map)
|
|
|
|
(general-nmap "C-p" 'counsel-projectile-find-file)
|
|
#+end_src
|
|
|
|
** Company
|
|
A completion framework for Emacs.
|
|
|
|
References:
|
|
- [[http://company-mode.github.io/][company homepage]]
|
|
- [[https://github.com/sebastiencs/company-box][company-box homepage]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package company
|
|
:straight t
|
|
:config
|
|
(global-company-mode)
|
|
(setq company-idle-delay (if my/lowpower 0.5 0.125))
|
|
(setq company-dabbrev-downcase nil)
|
|
(setq company-show-numbers t))
|
|
|
|
(general-imap "C-SPC" 'company-complete)
|
|
#+end_src
|
|
|
|
A company frontend with nice icons.
|
|
#+begin_src emacs-lisp
|
|
(use-package company-box
|
|
:straight t
|
|
:if (not my/lowpower)
|
|
:after (company)
|
|
:hook (company-mode . company-box-mode))
|
|
|
|
#+end_src
|
|
|
|
** Git & Magit
|
|
[[https://magit.vc/][Magic]] is a git interface for Emacs. The closest non-Emacs alternative (sans actual clones) I know is [[https://github.com/jesseduffield/lazygit][lazygit]], which I used before Emacs.
|
|
|
|
Also, [[https://github.com/emacsorphanage/git-gutter][git-gutter]] is plugin which shows git changes for each line (added/changed/deleted lines).
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package magit
|
|
:straight t
|
|
:commands (magit-status magit-file-dispatch)
|
|
:config
|
|
(setq magit-blame-styles
|
|
'((margin
|
|
(margin-format . ("%a %A %s"))
|
|
(margin-width . 42)
|
|
(margin-face . magit-blame-margin)
|
|
(margin-body-face . (magit-blame-dimmed)))
|
|
(headings
|
|
(heading-format . "%-20a %C %s\n"))
|
|
(highlight
|
|
(highlight-face . magit-blame-highlight))
|
|
(lines
|
|
(show-lines . t)
|
|
(show-message . t)))
|
|
))
|
|
|
|
(use-package git-gutter
|
|
:straight t
|
|
:if (not my/slow-ssh)
|
|
:config
|
|
(global-git-gutter-mode +1))
|
|
|
|
(my-leader-def
|
|
"m" 'magit
|
|
"M" 'magit-file-dispatch)
|
|
#+end_src
|
|
|
|
** Editorconfig
|
|
Editorconfig support for Emacs.
|
|
|
|
References:
|
|
- [[https://editorconfig.org/][Editorconfig reference]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package editorconfig
|
|
:straight t
|
|
:config
|
|
(unless my/slow-ssh (editorconfig-mode 1))
|
|
(add-to-list 'editorconfig-indentation-alist
|
|
'(emmet-mode emmet-indentation)))
|
|
#+end_src
|
|
|
|
** OFF (OFF) Avy
|
|
#+begin_src emacs-lisp :tangle no
|
|
(use-package avy
|
|
:straight t)
|
|
|
|
(general-nmap "\\w" 'avy-goto-word-0-below)
|
|
(general-nmap "\\b" 'avy-goto-word-0-above)
|
|
#+end_src
|
|
|
|
** Snippets
|
|
A snippet system for Emacs and a collection of pre-built snippets.
|
|
|
|
~yasnippet-snippets~ has to be loaded before ~yasnippet~ for user snippets to override the pre-built ones.
|
|
|
|
References:
|
|
- [[http://joaotavora.github.io/yasnippet/][yasnippet documentation]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package yasnippet-snippets
|
|
:straight t)
|
|
|
|
(use-package yasnippet
|
|
:straight t
|
|
:config
|
|
(setq yas-triggers-in-field t)
|
|
(yas-global-mode 1))
|
|
|
|
(general-imap "M-TAB" 'company-yasnippet)
|
|
#+end_src
|
|
** Time trackers
|
|
A bunch of timetrackers I use.
|
|
|
|
References:
|
|
- [[https://wakatime.com][WakaTime]]
|
|
- [[https://activitywatch.net/][ActivityWatch]]
|
|
|
|
*** WakaTime
|
|
Before I figure out how to package this for Guix:
|
|
- Clone [[https://github.com/wakatime/wakatime-cli][the repo]]
|
|
- Run ~go build~
|
|
- Copy the binary to the =~/bin= folder
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package wakatime-mode
|
|
:straight t
|
|
:config
|
|
(advice-add 'wakatime-init :after (lambda () (setq wakatime-cli-path "/home/pavel/bin/wakatime")))
|
|
(global-wakatime-mode))
|
|
#+end_src
|
|
*** ActivityWatch
|
|
#+begin_src emacs-lisp
|
|
(use-package request
|
|
:straight t)
|
|
|
|
(use-package activity-watch-mode
|
|
:straight t
|
|
:config
|
|
(global-activity-watch-mode))
|
|
#+end_src
|
|
* UI
|
|
** General UI & GUI Settings
|
|
Disable GUI elements
|
|
#+begin_src emacs-lisp
|
|
(tool-bar-mode -1)
|
|
(menu-bar-mode -1)
|
|
(scroll-bar-mode -1)
|
|
#+end_src
|
|
|
|
Transparency
|
|
#+begin_src emacs-lisp
|
|
;; (set-frame-parameter (selected-frame) 'alpha '(90 . 90))
|
|
;; (add-to-list 'default-frame-alist '(alpha . (90 . 90)))
|
|
#+end_src
|
|
|
|
Prettify symbols
|
|
#+begin_src emacs-lisp
|
|
;; (global-prettify-symbols-mode)
|
|
#+end_src
|
|
|
|
No start screen
|
|
#+begin_src emacs-lisp
|
|
(setq inhibit-startup-screen t)
|
|
#+end_src
|
|
|
|
Visual bell
|
|
#+begin_src emacs-lisp
|
|
(setq visible-bell 0)
|
|
#+end_src
|
|
|
|
y or n instead of yes or no
|
|
#+begin_src emacs-lisp
|
|
(defalias 'yes-or-no-p 'y-or-n-p)
|
|
#+end_src
|
|
|
|
Hide mouse cursor while typing
|
|
#+begin_src emacs-lisp
|
|
(setq make-pointer-invisible t)
|
|
#+end_src
|
|
|
|
Line numbers. There seems to be a catch with the relative number setting:
|
|
- =visual= doesn't take folding into account, but also doesn't take wrapped lines into account (makes multiple numbers for a single wrapped line)
|
|
- =relative= makes a single number for a wrapped line, but counts folded lines.
|
|
|
|
=visual= option seems to be less of a problem in most cases.
|
|
#+begin_src emacs-lisp
|
|
(global-display-line-numbers-mode 1)
|
|
(line-number-mode nil)
|
|
(setq display-line-numbers-type 'visual)
|
|
(column-number-mode)
|
|
#+end_src
|
|
|
|
Show pairs
|
|
#+begin_src emacs-lisp
|
|
(show-paren-mode 1)
|
|
#+end_src
|
|
|
|
Word wrap
|
|
#+begin_src emacs-lisp
|
|
(setq word-wrap 1)
|
|
(global-visual-line-mode t)
|
|
#+end_src
|
|
|
|
Hightlight line
|
|
#+begin_src emacs-lisp
|
|
(global-hl-line-mode 1)
|
|
#+end_src
|
|
** Theme & global stuff
|
|
Dim inactive buffers.
|
|
#+begin_src emacs-lisp
|
|
(use-package auto-dim-other-buffers
|
|
:straight t
|
|
:if (display-graphic-p)
|
|
:config
|
|
(set-face-attribute 'auto-dim-other-buffers-face nil
|
|
:background "#212533")
|
|
(auto-dim-other-buffers-mode t))
|
|
#+end_src
|
|
|
|
My colorscheme of choice.
|
|
#+begin_src emacs-lisp
|
|
(use-package doom-themes
|
|
:straight t
|
|
:config
|
|
(setq doom-themes-enable-bold t
|
|
doom-themes-enable-italic t)
|
|
(load-theme 'doom-palenight t)
|
|
(doom-themes-visual-bell-config)
|
|
(setq doom-themes-treemacs-theme "doom-colors")
|
|
(doom-themes-treemacs-config))
|
|
#+end_src
|
|
*** Custom theme
|
|
A custom theme, dependent on Doom. I set all my custom variables there.
|
|
|
|
A custom theme is necessary because if one calls =custom-set-faces= and =custom-set-variables= in code, whenever a variable is changed and saved in a customize buffer, data from all calls of these functions is saved as as well.
|
|
|
|
Also, a hook allows me to change doom-theme more or less at will, although I do that only to switch to a light theme once in a blue moon.
|
|
#+begin_src emacs-lisp
|
|
(deftheme my-theme)
|
|
|
|
(defun my/update-my-theme (&rest _)
|
|
(custom-theme-set-faces
|
|
'my-theme
|
|
`(tab-bar-tab ((t (
|
|
:background ,(doom-color 'bg)
|
|
:foreground ,(doom-color 'yellow)
|
|
:underline ,(doom-color 'yellow)))))
|
|
`(org-block ((t (:background ,(color-darken-name (doom-color 'bg) 3)))))
|
|
`(org-block-begin-line ((t (
|
|
:background ,(color-darken-name (doom-color 'bg) 3)
|
|
:foreground ,(doom-color 'grey)))))
|
|
`(auto-dim-other-buffers-face ((t (:background ,(color-darken-name (doom-color 'bg) 3)))))
|
|
`(aweshell-alert-buffer-face ((t (:foreground ,(doom-color 'red) :weight bold))))
|
|
`(aweshell-alert-command-face ((t (:foreground ,(doom-color 'yellow) :weight bold))))
|
|
`(epe-pipeline-delimiter-face ((t (:foreground ,(doom-color 'green)))))
|
|
`(epe-pipeline-host-face ((t (:foreground ,(doom-color 'blue)))))
|
|
`(epe-pipeline-time-face ((t (:foreground ,(doom-color 'yellow)))))
|
|
`(epe-pipeline-user-face ((t (:foreground ,(doom-color 'red)))))
|
|
`(elfeed-search-tag-face ((t (:foreground ,(doom-color 'yellow))))))
|
|
(custom-theme-set-variables
|
|
'my-theme
|
|
`(aweshell-invalid-command-color ,(doom-color 'red))
|
|
`(aweshell-valid-command-color ,(doom-color 'green)))
|
|
(enable-theme 'my-theme))
|
|
|
|
(advice-add 'load-theme :after #'my/update-my-theme)
|
|
(when (fboundp 'doom-color)
|
|
(my/update-my-theme))
|
|
#+end_src
|
|
|
|
*** Font
|
|
To install a font, download the font and unpack it into the =.local/share/fonts= directory. Create one if it doesn't exist.
|
|
|
|
As I use nerd fonts elsewhere, I use one in Emacs as well.
|
|
|
|
References:
|
|
- [[https://nerdfonts.com][nerd fonts homepage]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(set-frame-font "JetBrainsMono Nerd Font 10" nil t)
|
|
#+end_src
|
|
|
|
To make the icons work (e.g. in the Doom Modeline), run =M-x all-the-icons-install-fonts=. The package definition is somewhere later in the config.
|
|
** Custom frame title
|
|
#+begin_src emacs-lisp
|
|
(setq-default frame-title-format
|
|
'(""
|
|
"emacs"
|
|
(:eval
|
|
(let ((project-name (projectile-project-name)))
|
|
(if (not (string= "-" project-name))
|
|
(format ":%s@%s" project-name (system-name))
|
|
(format "@%s" (system-name)))))))
|
|
#+end_src
|
|
** Tab bar
|
|
I rely rather heavily on tab-bar in my workflow. I have a suspicion I'm not using it the intended way, but that works for me.
|
|
*** Setup
|
|
#+begin_src emacs-lisp
|
|
(general-define-key
|
|
:keymaps 'override
|
|
:states '(normal emacs)
|
|
"gt" 'tab-bar-switch-to-next-tab
|
|
"gT" 'tab-bar-switch-to-prev-tab
|
|
"gn" 'tab-bar-new-tab)
|
|
|
|
(setq tab-bar-show 1)
|
|
(setq tab-bar-tab-hints t)
|
|
(setq tab-bar-tab-name-function 'tab-bar-tab-name-current-with-count)
|
|
|
|
;; Tabs
|
|
(general-nmap "gn" 'tab-new)
|
|
(general-nmap "gN" 'tab-close)
|
|
#+end_src
|
|
*** My title
|
|
Prepend tab name with the shortened projectile project title
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq my/project-title-separators "[-_ ]")
|
|
|
|
(defun my/shorten-project-name-elem (elem crop)
|
|
(if (string-match "^\\[.*\\]$" elem)
|
|
(concat "["
|
|
(my/shorten-project-name-elem (substring elem 1 (- (length elem) 1)) crop)
|
|
"]")
|
|
(let* ((prefix (car (s-match my/project-title-separators elem)))
|
|
(rest
|
|
(substring
|
|
(if prefix
|
|
(substring elem (length prefix))
|
|
elem)
|
|
0 (if crop 1 nil))))
|
|
(concat prefix rest))))
|
|
|
|
(defun my/shorten-project-name (project-name)
|
|
(let ((elems (s-slice-at my/project-title-separators project-name)))
|
|
(concat
|
|
(apply
|
|
#'concat
|
|
(cl-mapcar (lambda (elem) (my/shorten-project-name-elem elem t)) (butlast elems)))
|
|
(my/shorten-project-name-elem (car (last elems)) nil))))
|
|
|
|
(defun my/tab-bar-name-function ()
|
|
(let ((project-name (projectile-project-name)))
|
|
(if (string= "-" project-name)
|
|
(tab-bar-tab-name-current-with-count)
|
|
(concat "[" (my/shorten-project-name project-name) "] "
|
|
(replace-regexp-in-string "<.*>" "" (tab-bar-tab-name-current-with-count))))))
|
|
|
|
(setq tab-bar-tab-name-function #'my/tab-bar-name-function)
|
|
#+end_src
|
|
|
|
** Modeline
|
|
A modeline from Doom Emacs.
|
|
|
|
References:
|
|
- [[https://github.com/seagle0128/doom-modeline][Doom Modeline]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package doom-modeline
|
|
:straight t
|
|
:init
|
|
(setq doom-modeline-env-enable-python nil)
|
|
(setq doom-modeline-env-enable-go nil)
|
|
:config
|
|
(doom-modeline-mode 1)
|
|
(setq doom-modeline-minor-modes nil)
|
|
(setq doom-modeline-buffer-state-icon nil))
|
|
#+end_src
|
|
** Font stuff
|
|
*** Emojis
|
|
| Note | Type |
|
|
|------+-----------------------------------------------------------|
|
|
| TODO | Figure out how to display emojis without prettify symbols |
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package emojify
|
|
:straight t
|
|
:if (not my/lowpower)
|
|
:hook (after-init . global-emojify-mode))
|
|
#+end_src
|
|
*** Ligatures
|
|
Ligature setup for the JetBrainsMono font.
|
|
#+begin_src emacs-lisp
|
|
(use-package ligature
|
|
:straight (:host github :repo "mickeynp/ligature.el")
|
|
:config
|
|
(ligature-set-ligatures
|
|
'(
|
|
typescript-mode
|
|
js2-mode
|
|
vue-mode
|
|
svelte-mode
|
|
scss-mode
|
|
php-mode
|
|
python-mode
|
|
js-mode
|
|
markdown-mode
|
|
clojure-mode
|
|
go-mode
|
|
sh-mode
|
|
haskell-mode)
|
|
'("--" "---" "==" "===" "!=" "!==" "=!=" "=:=" "=/=" "<="
|
|
">=" "&&" "&&&" "&=" "++" "+++" "***" ";;" "!!" "??"
|
|
"?:" "?." "?=" "<:" ":<" ":>" ">:" "<>" "<<<" ">>>"
|
|
"<<" ">>" "||" "-|" "_|_" "|-" "||-" "|=" "||=" "##"
|
|
"###" "####" "#{" "#[" "]#" "#(" "#?" "#_" "#_(" "#:"
|
|
"#!" "#=" "^=" "<$>" "<$" "$>" "<+>" "<+" "+>" "<*>"
|
|
"<*" "*>" "</" "</>" "/>" "<!--" "<#--" "-->" "->" "->>"
|
|
"<<-" "<-" "<=<" "=<<" "<<=" "<==" "<=>" "<==>" "==>" "=>"
|
|
"=>>" ">=>" ">>=" ">>-" ">-" ">--" "-<" "-<<" ">->" "<-<"
|
|
"<-|" "<=|" "|=>" "|->" "<->" "<~~" "<~" "<~>" "~~" "~~>"
|
|
"~>" "~-" "-~" "~@" "[||]" "|]" "[|" "|}" "{|" "[<"
|
|
">]" "|>" "<|" "||>" "<||" "|||>" "<|||" "<|>" "..." ".."
|
|
".=" ".-" "..<" ".?" "::" ":::" ":=" "::=" ":?" ":?>"
|
|
"//" "///" "/*" "*/" "/=" "//=" "/==" "@_" "__"))
|
|
(global-ligature-mode t))
|
|
#+end_src
|
|
*** Icons
|
|
#+begin_src emacs-lisp
|
|
(use-package all-the-icons
|
|
:straight t)
|
|
#+end_src
|
|
*** Highlight todo
|
|
#+begin_src emacs-lisp
|
|
(use-package hl-todo
|
|
:hook (prog-mode . hl-todo-mode)
|
|
:straight t)
|
|
#+end_src
|
|
** Text highlight improvements
|
|
Hightlight indent guides.
|
|
#+begin_src emacs-lisp
|
|
(use-package highlight-indent-guides
|
|
:straight t
|
|
:if (not my/lowpower)
|
|
:hook (
|
|
(prog-mode . highlight-indent-guides-mode)
|
|
(vue-mode . highlight-indent-guides-mode)
|
|
(LaTeX-mode . highlight-indent-guides-mode))
|
|
:config
|
|
(setq highlight-indent-guides-method 'bitmap)
|
|
(setq highlight-indent-guides-bitmap-function 'highlight-indent-guides--bitmap-line))
|
|
#+end_src
|
|
|
|
Rainbow parentheses.
|
|
#+begin_src emacs-lisp
|
|
(use-package rainbow-delimiters
|
|
:straight t
|
|
:if (not my/lowpower)
|
|
:hook ((prog-mode . rainbow-delimiters-mode))
|
|
;; :commands (rainbow-delimiters-mode)
|
|
;; :init
|
|
;; (add-hook 'prog-mode-hook
|
|
;; (lambda ()
|
|
;; (unless (org-in-src-block-p)
|
|
;; (rainbow-delimiters-mode))))
|
|
)
|
|
#+end_src
|
|
|
|
Highlight colors
|
|
#+begin_src emacs-lisp
|
|
(use-package rainbow-mode
|
|
:commands (rainbow-mode)
|
|
:straight t)
|
|
#+end_src
|
|
* Dired
|
|
Dired is a built-in file manager. I use it as my primary file manager, hence the top level of config.
|
|
|
|
| Type | Note |
|
|
|------+------------------------|
|
|
| TODO | Make subdirs mode work |
|
|
|
|
** Basic config & keybindings
|
|
My config mostly follows ranger's and vifm's keybindings which I'm used to.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package dired
|
|
:ensure nil
|
|
:custom ((dired-listing-switches "-alh --group-directories-first"))
|
|
:commands (dired)
|
|
:config
|
|
(setq dired-dwim-target t)
|
|
(setq wdired-allow-to-change-permissions t)
|
|
(setq wdired-create-parent-directories t)
|
|
(setq dired-recursive-copies 'always)
|
|
(setq dired-recursive-deletes 'always)
|
|
(add-hook 'dired-mode-hook
|
|
(lambda ()
|
|
(setq truncate-lines t)
|
|
(visual-line-mode nil)))
|
|
(evil-collection-define-key 'normal 'dired-mode-map
|
|
"h" 'dired-single-up-directory
|
|
"l" 'dired-single-buffer
|
|
"h" 'dired-single-up-directory
|
|
"l" 'dired-single-buffer
|
|
"=" 'dired-narrow
|
|
"-" 'dired-create-empty-file
|
|
"~" 'vterm
|
|
(kbd "<left>") 'dired-single-up-directory
|
|
(kbd "<right>") 'dired-single-buffer)
|
|
(general-define-key
|
|
:keymaps 'dired-mode-map
|
|
[remap dired-find-file] 'dired-single-buffer
|
|
[remap dired-mouse-find-file-other-window] 'dired-single-buffer-mouse
|
|
[remap dired-up-directory] 'dired-single-up-directory
|
|
"M-<return>" 'dired-open-xdg))
|
|
|
|
(defun my/dired-home ()
|
|
"Open dired at $HOME"
|
|
(interactive)
|
|
(dired (expand-file-name "~")))
|
|
|
|
(my-leader-def
|
|
"ad" #'dired
|
|
"aD" #'my/dired-home)
|
|
#+end_src
|
|
** Addons
|
|
[[https://www.emacswiki.org/emacs/DiredPlus][Dired+]] provides a lot of extensions for dired functionality.
|
|
#+begin_src emacs-lisp :noweb yes
|
|
(use-package dired+
|
|
:straight t
|
|
:init
|
|
(setq diredp-hide-details-initially-flag nil)
|
|
:config
|
|
<<diredp-fixes>>)
|
|
#+end_src
|
|
|
|
Reuse the current dired buffer instead of spamming new ones.
|
|
#+begin_src emacs-lisp
|
|
(use-package dired-single
|
|
:after dired
|
|
:straight t)
|
|
#+end_src
|
|
|
|
Display icons for files.
|
|
|
|
| Note | Type |
|
|
|-----------+-----------------------------------------|
|
|
| *ACHTUNG* | This plugin is slow as hell with TRAMP |
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package all-the-icons-dired
|
|
:straight t
|
|
:if (not (or my/lowpower my/slow-ssh))
|
|
:hook (dired-mode . all-the-icons-dired-mode)
|
|
:config
|
|
(advice-add 'dired-add-entry :around #'all-the-icons-dired--refresh-advice)
|
|
(advice-add 'dired-remove-entry :around #'all-the-icons-dired--refresh-advice))
|
|
#+end_src
|
|
|
|
Provides stuff like =dired-open-xdg=
|
|
#+begin_src emacs-lisp
|
|
(use-package dired-open
|
|
:straight t
|
|
:commands (dired-open-xdg))
|
|
#+end_src
|
|
|
|
vifm-like filter
|
|
#+begin_src emacs-lisp
|
|
(use-package dired-narrow
|
|
:straight t
|
|
:commands (dired-narrow)
|
|
:config
|
|
(general-define-key
|
|
:keymaps 'dired-narrow-map
|
|
[escape] 'keyboard-quit))
|
|
#+end_src
|
|
*** dired+ on Emacs 28
|
|
It looks like dired+ is not quite compatible with Emacs 28. So I override certain functions for now.
|
|
|
|
#+begin_src emacs-lisp :tangle no :noweb-ref diredp-fixes
|
|
(defun dired-do-delete (&optional arg)
|
|
"Delete all marked (or next ARG) files.
|
|
`dired-recursive-deletes' controls whether deletion of
|
|
non-empty directories is allowed."
|
|
;; This is more consistent with the file marking feature than
|
|
;; dired-do-flagged-delete.
|
|
(interactive "P")
|
|
(let (markers)
|
|
(dired-internal-do-deletions
|
|
(nreverse
|
|
;; this may move point if ARG is an integer
|
|
(dired-map-over-marks (cons (dired-get-filename)
|
|
(let ((m (point-marker)))
|
|
(push m markers)
|
|
m))
|
|
arg))
|
|
arg t)
|
|
(dolist (m markers) (set-marker m nil))))
|
|
#+end_src
|
|
|
|
** TRAMP
|
|
TRAMP is a package which provides remote editing capacities. It is particularly useful for remote server management.
|
|
|
|
One of the reasons why TRAMP may be slow is that some plugins do too much requests to the filesystem. To debug these issues, set the following variable to 6:
|
|
#+begin_src emacs-lisp
|
|
(setq tramp-verbose 1)
|
|
#+end_src
|
|
|
|
To check if a file is remote, you can use ~file-remote-p~. E.g. ~(file-remote-p default-directory)~ for a current buffer. The problem with this approach is that it's rather awkward to add these checks in every hook, especially for global modes, so for now I just set environment variable for Emacs which disables these modes.
|
|
|
|
So far I found the following problematic plugins:
|
|
| Plugin | Note | Solution |
|
|
|---------------------+------------------------------------------+-------------------------------|
|
|
| editorconfig | looks for .editorconfig in the file tree | do not enable globally |
|
|
| all-the-icons-dired | runs test on every file in the directory | disable |
|
|
| projectile | looks for .git, .svn, etc | advice ~projectile-file-name~ |
|
|
| lsp | does a whole lot of stuff | disable |
|
|
| git-gutter | runs git | disable |
|
|
| vterm | no proper TRAMP integration | use eshell or shell |
|
|
|
|
At any rate, it's usable, although not perfect.
|
|
|
|
Some other optimization settings:
|
|
#+begin_src emacs-lisp
|
|
(setq remote-file-name-inhibit-cache nil)
|
|
(setq vc-ignore-dir-regexp
|
|
(format "\\(%s\\)\\|\\(%s\\)"
|
|
vc-ignore-dir-regexp
|
|
tramp-file-name-regexp))
|
|
#+end_src
|
|
|
|
Also, here is a hack to make TRAMP find =ls= on Guix:
|
|
#+begin_src emacs-lisp
|
|
(with-eval-after-load 'tramp
|
|
(setq tramp-remote-path
|
|
(append tramp-remote-path
|
|
'(tramp-own-remote-path))))
|
|
#+end_src
|
|
** Bookmarks
|
|
A simple bookmark list for Dired, mainly to use with TRAMP. I may look into a proper bookmarking system later.
|
|
|
|
Bookmarks are listed in the [[file:.emacs.d/dired-bookmarks.el][dired-bookmarks.el]] file, which looks like this:
|
|
#+begin_example emacs-lisp :tangle no
|
|
(setq my/dired-bookmarks
|
|
'(("sudo" . "/sudo::/")))
|
|
#+end_example
|
|
|
|
The file itself is encrypted with yadm.
|
|
|
|
#+begin_src emacs-lisp
|
|
(defun my/dired-bookmark-open ()
|
|
(interactive)
|
|
(unless (boundp 'my/dired-bookmarks)
|
|
(load (concat user-emacs-directory "dired-bookmarks")))
|
|
(let ((bookmarks
|
|
(mapcar
|
|
(lambda (el) (cons (format "%-30s %s" (car el) (cdr el)) (cdr el)))
|
|
my/dired-bookmarks)))
|
|
(dired
|
|
(cdr
|
|
(assoc
|
|
(completing-read "Dired: " bookmarks nil nil "^")
|
|
bookmarks)))))
|
|
#+end_src
|
|
* Shells
|
|
** vterm
|
|
My terminal emulator of choice.
|
|
|
|
References:
|
|
- [[https://github.com/akermu/emacs-libvterm][emacs-libvterm repo]]
|
|
*** Configuration
|
|
I use the package from the Guix repository to avoid building libvterm.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package vterm
|
|
;; :straight t
|
|
:commands (vterm vterm-other-window)
|
|
:config
|
|
(setq vterm-kill-buffer-on-exit t)
|
|
|
|
(add-hook 'vterm-mode-hook
|
|
(lambda ()
|
|
(setq-local global-display-line-numbers-mode nil)
|
|
(display-line-numbers-mode 0)))
|
|
|
|
(general-define-key
|
|
:keymaps 'vterm-mode-map
|
|
"M-q" 'vterm-send-escape
|
|
|
|
"C-h" 'evil-window-left
|
|
"C-l" 'evil-window-right
|
|
"C-k" 'evil-window-up
|
|
"C-j" 'evil-window-down
|
|
|
|
"C-<right>" 'evil-window-right
|
|
"C-<left>" 'evil-window-left
|
|
"C-<up>" 'evil-window-up
|
|
"C-<down>" 'evil-window-down
|
|
|
|
"M-<left>" 'vterm-send-left
|
|
"M-<right>" 'vterm-send-right
|
|
"M-<up>" 'vterm-send-up
|
|
"M-<down>" 'vterm-send-down)
|
|
|
|
(general-imap
|
|
:keymaps 'vterm-mode-map
|
|
"C-r" 'vterm-send-C-r
|
|
"C-k" 'vterm-send-C-k
|
|
"C-j" 'vterm-send-C-j
|
|
"M-l" 'vterm-send-right
|
|
"M-h" 'vterm-send-left))
|
|
#+end_src
|
|
*** Subterminal
|
|
Open a terminal in the lower third of the frame with the =`= key. That's mostly how I use vterm.
|
|
|
|
#+begin_src emacs-lisp
|
|
(add-to-list 'display-buffer-alist
|
|
`(,"vterm-subterminal.*"
|
|
(display-buffer-reuse-window
|
|
display-buffer-in-side-window)
|
|
(side . bottom)
|
|
(reusable-frames . visible)
|
|
(window-height . 0.33)))
|
|
|
|
(defun my/toggle-vterm-subteminal ()
|
|
"Toogle subteminal."
|
|
(interactive)
|
|
(let
|
|
((vterm-window
|
|
(seq-find
|
|
(lambda (window)
|
|
(string-match
|
|
"vterm-subterminal.*"
|
|
(buffer-name (window-buffer window))))
|
|
(window-list))))
|
|
(if vterm-window
|
|
(if (eq (get-buffer-window (current-buffer)) vterm-window)
|
|
(kill-buffer (current-buffer))
|
|
(select-window vterm-window))
|
|
(vterm-other-window "vterm-subterminal"))))
|
|
(unless my/slow-ssh
|
|
(general-nmap "`" 'my/toggle-vterm-subteminal)
|
|
(general-nmap "~" 'vterm))
|
|
#+end_src
|
|
** Eshell
|
|
A shell written in Emacs lisp. I don't use it as of now, but keep the config just in case.
|
|
#+begin_src emacs-lisp
|
|
(defun my/configure-eshell ()
|
|
(add-hook 'eshell-pre-command-hook 'eshell-save-some-history)
|
|
(add-to-list 'eshell-output-filter-functions 'eshell-truncate-buffer)
|
|
(setq eshell-history-size 10000)
|
|
(setq eshell-hist-ingnoredups t)
|
|
(setq eshell-buffer-maximum-lines 10000)
|
|
|
|
(evil-define-key '(normal insert visual) eshell-mode-map (kbd "<home>") 'eshell-bol)
|
|
(evil-define-key '(normal insert visual) eshell-mode-map (kbd "C-r") 'counsel-esh-history)
|
|
(evil-collection-define-key 'normal 'eshell-mode-map
|
|
(kbd "C-h") 'evil-window-left
|
|
(kbd "C-l") 'evil-window-right
|
|
(kbd "C-k") 'evil-window-up
|
|
(kbd "C-j") 'evil-window-down))
|
|
|
|
(use-package eshell
|
|
:ensure nil
|
|
:after evil-collection
|
|
:commands (eshell)
|
|
:config
|
|
(add-hook 'eshell-first-time-mode-hook 'my/configure-eshell 90)
|
|
(when my/slow-ssh
|
|
(add-hook 'eshell-mode-hook
|
|
(lambda ()
|
|
(setq-local company-idle-delay 1000))))
|
|
(setq eshell-banner-message ""))
|
|
|
|
(use-package aweshell
|
|
:straight (:repo "manateelazycat/aweshell" :host github)
|
|
:after eshell
|
|
:config
|
|
(setq eshell-highlight-prompt nil)
|
|
(setq eshell-prompt-function 'epe-theme-pipeline))
|
|
|
|
(use-package eshell-info-banner
|
|
:defer t
|
|
:if (not my/slow-ssh)
|
|
:straight (eshell-info-banner :type git
|
|
:host github
|
|
:repo "phundrak/eshell-info-banner.el")
|
|
:hook (eshell-banner-load . eshell-info-banner-update-banner))
|
|
|
|
(when my/slow-ssh
|
|
(general-nmap "`" 'aweshell-dedicated-toggle)
|
|
(general-nmap "~" 'eshell))
|
|
#+end_src
|
|
* Org Mode
|
|
The best feature of Emacs. Just after every other best feature of Emacs, probably.
|
|
|
|
References:
|
|
- [[https://orgmode.org/][Org Mode homepage]]
|
|
- [[https://orgmode.org/manual/][Manual]]
|
|
|
|
** Installation & basic settings
|
|
Use the built-in org mode.
|
|
|
|
#+begin_src emacs-lisp :noweb yes
|
|
(use-package org
|
|
:straight t
|
|
:defer t
|
|
:config
|
|
(setq org-startup-indented t)
|
|
(setq org-return-follows-link t)
|
|
(setq org-src-tab-acts-natively nil)
|
|
(add-hook 'org-mode-hook 'smartparens-mode)
|
|
(add-hook 'org-agenda-mode-hook
|
|
(lambda ()
|
|
(visual-line-mode -1)
|
|
(toggle-truncate-lines 1)
|
|
(display-line-numbers-mode 0)))
|
|
(add-hook 'org-mode-hook
|
|
(lambda ()
|
|
(rainbow-delimiters-mode -1)))
|
|
<<org-crypt-setup>>
|
|
<<org-lang-setup>>
|
|
<<org-ui-setup>>
|
|
<<org-keys-setup>>
|
|
<<org-productivity-setup>>)
|
|
#+end_src
|
|
|
|
*** Encryption
|
|
#+begin_src emacs-lisp :noweb-ref org-crypt-setup
|
|
(require 'org-crypt)
|
|
(org-crypt-use-before-save-magic)
|
|
(setq org-tags-exclude-from-inheritance (quote ("crypt")))
|
|
(setq org-crypt-key "C1EC867E478472439CC82410DE004F32AFA00205")
|
|
#+end_src
|
|
*** org-contrib
|
|
=org-contrib= is a package with various additions to Org. I use the following:
|
|
- =ox-extra= - extensions for org export
|
|
- =ol-notmuch= - integration with notmuch
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package org-contrib
|
|
:straight (org-contrib
|
|
:type git
|
|
:host nil
|
|
:repo "https://git.sr.ht/~bzg/org-contrib"
|
|
:build t)
|
|
:after (org)
|
|
:config
|
|
(require 'ox-extra)
|
|
(require 'ol-notmuch)
|
|
(ox-extras-activate '(latex-header-blocks ignore-headlines)))
|
|
#+end_src
|
|
** Integration with evil
|
|
#+begin_src emacs-lisp
|
|
(use-package evil-org
|
|
:straight t
|
|
:hook (org-mode . evil-org-mode)
|
|
:config
|
|
(add-hook 'evil-org-mode-hook
|
|
(lambda ()
|
|
(evil-org-set-key-theme '(navigation insert textobjects additional calendar todo))))
|
|
(add-to-list 'evil-emacs-state-modes 'org-agenda-mode)
|
|
(require 'evil-org-agenda)
|
|
(evil-org-agenda-set-keys))
|
|
#+end_src
|
|
** Literate programing
|
|
*** Python & Jupyter
|
|
Use jupyter kernels for Org Mode.
|
|
|
|
References:
|
|
- [[https://github.com/nnicandro/emacs-jupyter][emacs-jupyter repo]]
|
|
- [[https://github.com/jkitchin/scimax/blob/master/scimax.org][SCIMAX manual]]
|
|
|
|
#+begin_src emacs-lisp :noweb-ref org-lang-setup
|
|
(use-package jupyter
|
|
:straight t
|
|
:init
|
|
(my-leader-def "ar" 'jupyter-run-repl))
|
|
#+end_src
|
|
|
|
Refresh kernelspecs.
|
|
|
|
Kernelspecs by default are hashed, so even switching Anaconda environments doesn't change kernel (i.e. kernel from the first environment is ran after the switch to the second one).
|
|
|
|
#+begin_src emacs-lisp
|
|
(defun my/jupyter-refresh-kernelspecs ()
|
|
"Refresh Jupyter kernelspecs"
|
|
(interactive)
|
|
(jupyter-available-kernelspecs t))
|
|
#+end_src
|
|
|
|
Also, if some kernel wasn't present an the moment of load of =emacs-jupyter=, it won't be added to the =org-src-lang-modes= list. E.g. I have Hy kernel installed in a separate Anaconda environment, so if Emacs hasn't been launched in this environment, I wouldn't be able to use =hy= in org-src blocks.
|
|
|
|
Fortunately, =emacs-jupyter= provides a function for that problem as well.
|
|
#+begin_src emacs-lisp
|
|
(defun my/jupyter-refesh-langs ()
|
|
"Refresh Jupyter languages"
|
|
(interactive)
|
|
(org-babel-jupyter-aliases-from-kernelspecs t))
|
|
#+end_src
|
|
*** Hy
|
|
#+begin_src emacs-lisp :noweb-ref org-lang-setup
|
|
(use-package ob-hy
|
|
:straight t)
|
|
#+end_src
|
|
*** View HTML in browser
|
|
Open HTML in the ~begin_export~ block with xdg-open.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq my/org-view-html-tmp-dir "/tmp/org-html-preview/")
|
|
|
|
(use-package f
|
|
:straight t)
|
|
|
|
(defun my/org-view-html ()
|
|
(interactive)
|
|
(let ((elem (org-element-at-point))
|
|
(temp-file-path (concat my/org-view-html-tmp-dir (number-to-string (random (expt 2 32))) ".html")))
|
|
(cond
|
|
((not (eq 'export-block (car elem)))
|
|
(message "Not in an export block!"))
|
|
((not (string-equal (plist-get (car (cdr elem)) :type) "HTML"))
|
|
(message "Export block is not HTML!"))
|
|
(t (progn
|
|
(f-mkdir my/org-view-html-tmp-dir)
|
|
(f-write (plist-get (car (cdr elem)) :value) 'utf-8 temp-file-path)
|
|
(start-process "org-html-preview" nil "xdg-open" temp-file-path))))))
|
|
#+end_src
|
|
*** Setup
|
|
Enable languages
|
|
#+begin_src emacs-lisp :tangle no :noweb-ref org-lang-setup
|
|
(org-babel-do-load-languages
|
|
'org-babel-load-languages
|
|
'((emacs-lisp . t)
|
|
(python . t)
|
|
(sql . t)
|
|
;; (typescript .t)
|
|
(hy . t)
|
|
(shell . t)
|
|
(octave . t)
|
|
(jupyter . t)))
|
|
|
|
(add-hook 'org-babel-after-execute-hook 'org-redisplay-inline-images)
|
|
#+end_src
|
|
|
|
Use Jupyter block instead of built-in Python.
|
|
#+begin_src emacs-lisp :tangle no :noweb-ref org-lang-setup
|
|
(org-babel-jupyter-override-src-block "python")
|
|
(org-babel-jupyter-override-src-block "hy")
|
|
#+end_src
|
|
|
|
Turn of some minor modes in source blocks.
|
|
#+begin_src emacs-lisp :tangle no :noweb-ref org-lang-setup
|
|
(add-hook 'org-src-mode-hook
|
|
(lambda ()
|
|
;; (hs-minor-mode -1)
|
|
;; (electric-indent-local-mode -1)
|
|
;; (rainbow-delimiters-mode -1)
|
|
(highlight-indent-guides-mode -1)))
|
|
#+end_src
|
|
|
|
Async code blocks evaluations. Jupyter blocks have a built-in async, so they are set as ignored.
|
|
#+begin_src emacs-lisp
|
|
(use-package ob-async
|
|
:straight t
|
|
:after (org)
|
|
:config
|
|
(setq ob-async-no-async-languages-alist '("python" "hy" "jupyter-python" "jupyter-octave")))
|
|
#+end_src
|
|
*** Managing Jupyter kernels
|
|
Functions for managing local Jupyter kernels.
|
|
|
|
~my/insert-jupyter-kernel~ inserts a path to an active Jupyter kernel to the buffer. Useful to quickly write a header like:
|
|
#+begin_example
|
|
#+PROPERTY: header-args:python :session <path-to-kernel>
|
|
#+end_example
|
|
|
|
~my/jupyter-connect-repl~ opens a =emacs-jupyter= REPL, connected to an active kernel. ~my/jupyter-qtconsole~ runs a standalone Jupyter QtConsole.
|
|
|
|
Requirements: =ss=
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq my/jupyter-runtime-folder (expand-file-name "~/.local/share/jupyter/runtime"))
|
|
|
|
(defun my/get-open-ports ()
|
|
(mapcar
|
|
#'string-to-number
|
|
(split-string (shell-command-to-string "ss -tulpnH | awk '{print $5}' | sed -e 's/.*://'") "\n")))
|
|
|
|
(defun my/list-jupyter-kernel-files ()
|
|
(mapcar
|
|
(lambda (file) (cons (car file) (cdr (assq 'shell_port (json-read-file (car file))))))
|
|
(sort
|
|
(directory-files-and-attributes my/jupyter-runtime-folder t ".*kernel.*json$")
|
|
(lambda (x y) (not (time-less-p (nth 6 x) (nth 6 y)))))))
|
|
|
|
(defun my/select-jupyter-kernel ()
|
|
(let ((ports (my/get-open-ports))
|
|
(files (my/list-jupyter-kernel-files)))
|
|
(completing-read
|
|
"Jupyter kernels: "
|
|
(seq-filter
|
|
(lambda (file)
|
|
(member (cdr file) ports))
|
|
files))))
|
|
|
|
(defun my/insert-jupyter-kernel ()
|
|
"Insert a path to an active Jupyter kernel into the buffer"
|
|
(interactive)
|
|
(insert (my/select-jupyter-kernel)))
|
|
|
|
(defun my/jupyter-connect-repl ()
|
|
"Open an emacs-jupyter REPL, connected to a Jupyter kernel"
|
|
(interactive)
|
|
(jupyter-connect-repl (my/select-jupyter-kernel) nil nil nil t))
|
|
|
|
(defun my/jupyter-qtconsole ()
|
|
"Open Jupyter QtConsole, connected to a Jupyter kernel"
|
|
(interactive)
|
|
(start-process "jupyter-qtconsole" nil "setsid" "jupyter" "qtconsole" "--existing"
|
|
(file-name-nondirectory (my/select-jupyter-kernel))))
|
|
#+end_src
|
|
|
|
I've also noticed that there are JSON files left in the runtime folder whenever kernel isn't stopped correctly. So here is a cleanup function.
|
|
|
|
#+begin_src emacs-lisp
|
|
(defun my/jupyter-cleanup-kernels ()
|
|
(interactive)
|
|
(let* ((ports (my/get-open-ports))
|
|
(files (my/list-jupyter-kernel-files))
|
|
(to-delete (seq-filter
|
|
(lambda (file)
|
|
(not (member (cdr file) ports)))
|
|
files)))
|
|
(when (and (length> to-delete 0)
|
|
(y-or-n-p (format "Delete %d files?" (length to-delete))))
|
|
(dolist (file to-delete)
|
|
(delete-file (car file))))))
|
|
#+end_src
|
|
*** Do not wrap output in emacs-jupyter
|
|
Emacs-jupyter has its own insertion mechanisms, which always prepents output statements with =:=. That is not desirable in cases where a kernel supports only plain output, e.g. calysto_hy kernel.
|
|
|
|
So there we have a minor mode which overrides this behavior.
|
|
|
|
#+begin_src emacs-lisp
|
|
(defun my/jupyter-org-scalar (value)
|
|
(cond
|
|
((stringp value) value)
|
|
(t (jupyter-org-scalar value))))
|
|
|
|
(define-minor-mode my/emacs-jupyter-raw-output
|
|
"Make emacs-jupyter do raw output")
|
|
|
|
(defun my/jupyter-org-scalar-around (fun value)
|
|
(if my/emacs-jupyter-raw-output
|
|
(my/jupyter-org-scalar value)
|
|
(funcall fun value)))
|
|
|
|
(advice-add 'jupyter-org-scalar :around #'my/jupyter-org-scalar-around)
|
|
#+end_src
|
|
*** Wrap source code output
|
|
A function to remove :RESULTS: drawer from the results. Once again, necessary because emacs-jupyter doesn't seem to respect :results raw.
|
|
#+begin_src emacs-lisp
|
|
(defun my/org-strip-results (data)
|
|
(replace-regexp-in-string ":\\(RESULTS\\|END\\):\n" "" data))
|
|
#+end_src
|
|
|
|
And an all-in-one function to:
|
|
- prepend =#+NAME:= and =#+CAPTION:= to the source block output. Useful if the output is image.
|
|
- strip :RESULTS: drawer from the output, if necessary
|
|
- wrap results in the =src= block
|
|
|
|
As for now, looks sufficient to format source code outputs to get a tolerable LaTeX.
|
|
|
|
#+begin_src emacs-lisp
|
|
(defun my/org-caption-wrap (data &optional name caption attrs strip-drawer src-wrap)
|
|
(let* ((data-s (if (and strip-drawer (not (string-empty-p strip-drawer)))
|
|
(my/org-strip-results data)
|
|
data))
|
|
(drawer-start (if (string-match-p "^:RESULTS:.*" data-s) 10 0)))
|
|
(concat
|
|
(substring data-s 0 drawer-start)
|
|
(and name (not (string-empty-p name)) (concat "#+NAME:" name "\n"))
|
|
(and caption (not (string-empty-p caption)) (concat "#+CAPTION:" caption "\n"))
|
|
(and attrs (not (string-empty-p attrs)) (concat "#+ATTR_LATEX:" attrs "\n"))
|
|
(if (and src-wrap (not (string-empty-p src-wrap)))
|
|
(concat "#+begin_src " src-wrap "\n"
|
|
(substring data-s drawer-start)
|
|
(when (not (string-match-p ".*\n" data-s)) "\n")
|
|
"#+end_src")
|
|
(substring data-s drawer-start)))))
|
|
#+end_src
|
|
|
|
To use, add the following snippet to the org file:
|
|
#+begin_example
|
|
#+NAME: out_wrap
|
|
#+begin_src emacs-lisp :var data="" caption="" name="" attrs="" strip-drawer="" src-wrap="" :tangle no :exports none
|
|
(my/org-caption-wrap data name caption attrs strip-drawer src-wrap)
|
|
#+end_src
|
|
#+end_example
|
|
|
|
Example usage:
|
|
#+begin_example
|
|
:post out_wrap(name="fig:chart", caption="График", data=*this*)
|
|
#+end_example
|
|
** Productivity & Knowledge management
|
|
My on-going effort to get a productivity setup in Org.
|
|
|
|
Some inspiration:
|
|
- [[https://www.labri.fr/perso/nrougier/GTD/index.html][Nicolas P. Rougier. Get Things Done with Emacs]]
|
|
- [[https://blog.jethro.dev/posts/org_mode_workflow_preview/][Jetro Kuan. Org-mode Workflow]]
|
|
- [[https://www.alexeyshmalko.com/how-i-note/][Alexey Shmalko: How I note]]
|
|
- [[https://rgoswami.me/posts/org-note-workflow/][Rohit Goswami: An Orgmode Note Workflow]]
|
|
|
|
Used files
|
|
#+begin_src emacs-lisp :tangle no :noweb-ref org-productivity-setup
|
|
(setq org-directory (expand-file-name "~/Documents/org-mode"))
|
|
(setq org-agenda-files '("inbox.org" "projects.org" "work.org"))
|
|
;; (setq org-default-notes-file (concat org-directory "/notes.org"))
|
|
#+end_src
|
|
|
|
Hotkeys
|
|
#+begin_src emacs-lisp
|
|
(my-leader-def "oc" 'org-capture)
|
|
(my-leader-def "oa" 'org-agenda)
|
|
#+end_src
|
|
|
|
Refile targets
|
|
#+begin_src emacs-lisp
|
|
(setq org-refile-targets
|
|
'(("projects.org" :maxlevel . 2)))
|
|
(setq org-refile-use-outline-path 'file)
|
|
(setq org-outline-path-complete-in-steps nil)
|
|
#+end_src
|
|
|
|
*** Capture templates & various settings
|
|
Settings for Org capture mode. The goal here is to have a non-disruptive process to capture various ideas.
|
|
|
|
#+begin_src emacs-lisp :tangle no :noweb-ref org-productivity-setup
|
|
(setq org-capture-templates
|
|
`(("i" "Inbox" entry (file "inbox.org")
|
|
,(concat "* TODO %?\n"
|
|
"/Entered on/ %U"))
|
|
("e" "email" entry (file "inbox.org")
|
|
,(concat "* TODO %:from %:subject \n"
|
|
"/Entered on/ %U\n"
|
|
"/Received on/ %:date-timestamp-inactive\n"
|
|
"%a\n"))
|
|
("f" "elfeed" entry (file "inbox.org")
|
|
,(concat "* TODO %:elfeed-entry-title\n"
|
|
"/Entered on/ %U\n"
|
|
"%a\n"))))
|
|
#+end_src
|
|
|
|
Effort estimation
|
|
#+begin_src emacs-lisp :tangle no :noweb-ref org-productivity-setup
|
|
(add-to-list 'org-global-properties
|
|
'("Effort_ALL" . "0 0:05 0:10 0:15 0:30 0:45 1:00 2:00 4:00"))
|
|
#+end_src
|
|
|
|
Log DONE time
|
|
#+begin_src emacs-lisp :tangle no :noweb-ref org-productivity-setup
|
|
(setq org-log-done 'time)
|
|
#+end_src
|
|
*** Custom agendas
|
|
#+begin_src emacs-lisp
|
|
(defun my/org-scheduled-get-time ()
|
|
(let ((scheduled (org-get-scheduled-time (point))))
|
|
(if scheduled
|
|
(format-time-string "%Y-%m-%d" scheduled)
|
|
"")))
|
|
|
|
(setq org-agenda-custom-commands
|
|
`(("p" "My outline"
|
|
((agenda "")
|
|
(todo "NEXT"
|
|
((org-agenda-prefix-format " %i %-12:c [%e] ")
|
|
(org-agenda-overriding-header "Next tasks")))
|
|
(tags-todo "inbox"
|
|
((org-agenda-overriding-header "Inbox")
|
|
(org-agenda-prefix-format " %i %-12:c")
|
|
(org-agenda-hide-tags-regexp ".")))
|
|
(tags-todo "+waitlist+SCHEDULED<=\"<+14>\""
|
|
((org-agenda-overriding-header "Waitlist")
|
|
(org-agenda-hide-tags-regexp "waitlist")
|
|
(org-agenda-prefix-format " %i %-12:c %-12(my/org-scheduled-get-time)")))))
|
|
("tp" "Personal tasks"
|
|
((tags-todo "personal"
|
|
((org-agenda-prefix-format " %i %-12:c [%e] ")))))))
|
|
#+end_src
|
|
*** Org Journal
|
|
[[https://github.com/bastibe/org-journal][org-journal]] is a plugin for maintaining journal in org mode. I want to have its entries separate from my potential base.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package org-journal
|
|
:straight t
|
|
:after org
|
|
:config
|
|
(setq org-journal-dir (concat org-directory "/journal"))
|
|
(setq org-journal-file-type 'weekly)
|
|
(setq org-journal-file-format "%Y-%m-%d.org")
|
|
(setq org-journal-date-format "%A, %Y-%m-%d")
|
|
(setq org-journal-enable-encryption t))
|
|
|
|
(my-leader-def
|
|
:infix "oj"
|
|
"j" 'org-journal-new-entry
|
|
"o" 'org-journal-open-current-journal-file
|
|
"s" 'org-journal-search)
|
|
#+end_src
|
|
*** Org Roam
|
|
[[https://github.com/org-roam/org-roam][org-roam]] is a plain-text knowledge database.
|
|
|
|
| Guix dependency |
|
|
|-----------------------|
|
|
| emacs-emacsql-sqlite3 |
|
|
| graphviz |
|
|
|
|
References:
|
|
- [[https://github.com/org-roam/org-roam/wiki/Hitchhiker%27s-Rough-Guide-to-Org-roam-V2][Hitchhiker's Rough Guide to Org roam V2]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package emacsql-sqlite
|
|
:defer t
|
|
:straight (:type built-in))
|
|
|
|
(use-package org-roam
|
|
:straight t
|
|
:after org
|
|
:init
|
|
(setq org-roam-directory (concat org-directory "/roam"))
|
|
(setq org-roam-file-extensions '("org"))
|
|
(setq org-roam-v2-ack t)
|
|
(setq orb-insert-interface 'ivy-bibtex)
|
|
:config
|
|
(org-roam-setup)
|
|
(setq org-roam-capture-templates
|
|
`(("d" "default" plain "%?"
|
|
:if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n")
|
|
:unnarrowed t)))
|
|
(require 'org-roam-protocol))
|
|
|
|
(my-leader-def
|
|
:infix "or"
|
|
"i" 'org-roam-node-insert
|
|
"r" 'org-roam-node-find
|
|
"g" 'org-roam-graph
|
|
"c" 'org-roam-capture
|
|
"b" 'org-roam-buffer-toggle)
|
|
|
|
(with-eval-after-load 'org
|
|
(my-leader-def
|
|
:keymap 'org-mode-map
|
|
:infix "or"
|
|
"t" 'org-roam-tag-add
|
|
"T" 'org-toam-tag-remove)
|
|
(general-define-key
|
|
:keymap 'org-mode-map
|
|
"C-c i" 'org-id-get-create
|
|
"C-c l o" 'org-roam-node-insert))
|
|
#+end_src
|
|
**** org-roam-protocol
|
|
Open links such as =org-protocol://= from browser. Run =M-x server-start= for org-protocol to work.
|
|
|
|
#+begin_src conf :tangle ~/.local/share/applications/org-protocol.desktop
|
|
[Desktop Entry]
|
|
Name=Org-Protocol
|
|
Exec=emacsclient %u
|
|
Icon=emacs-icon
|
|
Type=Application
|
|
Terminal=false
|
|
MimeType=x-scheme-handler/org-protocol
|
|
#+end_src
|
|
|
|
Don't forget to run the following after setup:
|
|
#+begin_src bash :tangle no
|
|
xdg-mime default org-protocol.desktop x-scheme-handler/org-protocol
|
|
#+end_src
|
|
*** org-ref
|
|
| Type | Description |
|
|
|------+---------------------------------|
|
|
| TODO | Figure out how not to load Helm |
|
|
|
|
[[https://github.com/jkitchin/org-ref][org-ref]] is a package which provides support for various citations & reference in Org mode.
|
|
|
|
Useful to use BibTeX citations in LaTeX export.
|
|
|
|
As of now, this package loads Helm on start. To avoid this, I have to exclude Helm from the =Package-requires= in the [[file:.emacs.d/straight/repos/org-ref/org-ref.el][org-ref.el]] file. I haven't found a way to do this without modifying the package source yet.
|
|
#+begin_src emacs-lisp
|
|
(use-package org-ref
|
|
:straight (:files (:defaults (:exclude "*helm*")))
|
|
:init
|
|
(setq org-ref-completion-library 'org-ref-ivy-cite)
|
|
(setq bibtex-dialect 'biblatex)
|
|
(setq org-ref-default-bibliography '("~/Documents/org-mode/bibliography.bib"))
|
|
(setq reftex-default-bibliography org-ref-default-bibliography)
|
|
(setq bibtex-completion-bibliography org-ref-default-bibliography)
|
|
:after (org)
|
|
:config
|
|
(general-define-key
|
|
:keymaps 'org-mode-map
|
|
"C-c l l" 'org-ref-ivy-insert-cite-link
|
|
"C-c l r" 'org-ref-ivy-insert-ref-link
|
|
"C-c l h" 'org-ref-cite-hydra/body)
|
|
(general-define-key
|
|
:keymaps 'bibtex-mode-map
|
|
"M-RET" 'org-ref-bibtex-hydra/body)
|
|
(add-to-list 'orhc-candidate-formats
|
|
'("online" . " |${=key=}| ${title} ${url}")))
|
|
#+end_src
|
|
|
|
*** org-roam-bibtex
|
|
Integration with bibtex and org-ref.
|
|
|
|
There are some problems with org roam v2, so I disabled it as of now. I will probably use another way of managing bibliography notes anyway.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package org-roam-bibtex
|
|
:straight (:host github :repo "org-roam/org-roam-bibtex")
|
|
:after (org-roam org-ref)
|
|
:disabled
|
|
:config
|
|
(org-roam-bibtex-mode))
|
|
#+end_src
|
|
*** autocommit
|
|
A script to autocommit files in my org directory.
|
|
|
|
- If there are changed files and no files were changed over the last 60 minutes, commit.
|
|
- If there is an unpushed commit, push
|
|
- If either of these happened, make a notification
|
|
|
|
#+begin_src bash :tangle ~/Documents/org-mode/commit.sh
|
|
TIMEOUT_MIN=60
|
|
|
|
export DISPLAY=:0
|
|
cd $HOME/Documents/org-mode
|
|
|
|
TIMESTAMP=$(date +%s)
|
|
LAST_COMMIT_TIMESTAMP=$(git log -1 --format="%at" | xargs -I{} date -d @{} +%s)
|
|
RECENTLY_CHANGED_NUM=$(find . -not -path '*/\.*' -mmin -$TIMEOUT_MIN | wc -l)
|
|
CHANGED_NUM=$(git diff-index --name-only HEAD -- | wc -l)
|
|
COMMITED="No"
|
|
PUSHED="No"
|
|
|
|
if [[ ($RECENTLY_CHANGED_NUM -eq 0 || $1 = "-F") && $CHANGED_NUM -gt 0 ]]; then
|
|
read -r -d '' MESSAGE << EOM
|
|
Autocommit $(date -Iminutes)
|
|
|
|
Hostname: $(hostname)
|
|
EOM
|
|
git add *
|
|
git commit -m "$MESSAGE"
|
|
COMMITED="Yes"
|
|
fi
|
|
|
|
if [ $(git log origin/master..HEAD | wc -l) -gt 0 ]; then
|
|
git push && PUSHED="Yes" || PUSHED="No"
|
|
fi
|
|
|
|
if [[ $PUSHED = "Yes" || $COMMITED = "Yes" ]]; then
|
|
read -r -d '' NOTIFICATION << EOM
|
|
Commited: $COMMITED
|
|
Pushed: $PUSHED
|
|
EOM
|
|
notify-send "Org mode sync" "$NOTIFICATION"
|
|
fi
|
|
#+end_src
|
|
|
|
Cron job:
|
|
#+begin_src scheme :tangle ~/.config/cron/org-mode.guile
|
|
(job "0 * * * *" "~/Documents/org-mode/commit.sh")
|
|
#+end_src
|
|
** UI
|
|
*** OFF (OFF) Instant equations preview
|
|
Instant math previews for org mode.
|
|
|
|
References:
|
|
- [[https://github.com/yangsheng6810/org-latex-impatient][org-latex-impatient repo]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package org-latex-impatient
|
|
:straight (:repo "yangsheng6810/org-latex-impatient"
|
|
:branch "master"
|
|
:host github)
|
|
:hook (org-mode . org-latex-impatient-mode)
|
|
:disabled
|
|
:init
|
|
(setq org-latex-impatient-tex2svg-bin
|
|
"/home/pavel/Programs/miniconda3/lib/node_modules/mathjax-node-cli/bin/tex2svg")
|
|
(setq org-latex-impatient-scale 1.75)
|
|
(setq org-latex-impatient-delay 1)
|
|
(setq org-latex-impatient-border-color "#ffffff"))
|
|
#+end_src
|
|
*** LaTeX fragments
|
|
A function to enable LaTeX native highlighting. Not setting as default, because it loads LaTeX stuff.
|
|
#+begin_src emacs-lisp
|
|
(defun my/enable-org-latex ()
|
|
(interactive)
|
|
(customize-set-variable 'org-highlight-latex-and-related '(native))
|
|
(add-hook 'org-mode-hook (lambda () (yas-activate-extra-mode 'LaTeX-mode)))
|
|
(sp-local-pair 'org-mode "$" "$")
|
|
(sp--remove-local-pair "'"))
|
|
#+end_src
|
|
|
|
Call the function before opening an org file or reopen a buffer after calling the function.
|
|
|
|
Scale latex fragments preview.
|
|
#+begin_src emacs-lisp :noweb-ref org-ui-setup :tangle no
|
|
(setq my/org-latex-scale 1.75)
|
|
(setq org-format-latex-options (plist-put org-format-latex-options :scale my/org-latex-scale))
|
|
#+end_src
|
|
|
|
Also, LaTeX fragments preview tends to break whenever the are custom =#+LATEX_HEADER= entries. To circuvment this, I add a custom header and modify the ~org-preview-latex-process-alist~ variable
|
|
#+begin_src emacs-lisp :noweb-ref org-ui-setup :tangle no
|
|
(setq my/latex-preview-header "\\documentclass{article}
|
|
\\usepackage[usenames]{color}
|
|
\\usepackage{graphicx}
|
|
\\usepackage{grffile}
|
|
\\usepackage{longtable}
|
|
\\usepackage{wrapfig}
|
|
\\usepackage{rotating}
|
|
\\usepackage[normalem]{ulem}
|
|
\\usepackage{amsmath}
|
|
\\usepackage{textcomp}
|
|
\\usepackage{amssymb}
|
|
\\usepackage{capt-of}
|
|
\\usepackage{hyperref}
|
|
\\pagestyle{empty}")
|
|
|
|
(setq org-preview-latex-process-alist
|
|
(mapcar
|
|
(lambda (item)
|
|
(cons
|
|
(car item)
|
|
(plist-put (cdr item) :latex-header my/latex-preview-header)))
|
|
org-preview-latex-process-alist))
|
|
#+end_src
|
|
*** Better headers
|
|
#+begin_src emacs-lisp
|
|
(use-package org-superstar
|
|
:straight t
|
|
:hook (org-mode . org-superstar-mode))
|
|
#+end_src
|
|
*** Org Agenda Icons
|
|
Categories are broad labels to group agenda items.
|
|
|
|
#+begin_src emacs-lisp :noweb-ref org-ui-setup :tangle no
|
|
(if (not my/lowpower)
|
|
(setq org-agenda-category-icon-alist
|
|
`(("inbox" ,(list (all-the-icons-faicon "inbox")) nil nil :ascent center)
|
|
("work" ,(list (all-the-icons-faicon "cog")) nil nil :ascent center)
|
|
("education" ,(list (all-the-icons-material "build")) nil nil :ascent center)
|
|
("personal" ,(list (all-the-icons-faicon "music")) nil nil :ascent center)
|
|
("misc" ,(list (all-the-icons-material "archive")) nil nil :ascent center)
|
|
;; ("lesson" ,(list (all-the-icons-faicon "book")) nil nil :ascent center)
|
|
;; ("meeting" ,(list (all-the-icons-material "chat")) nil nil :ascent center)
|
|
;; ("event" ,(list (all-the-icons-octicon "clock")) nil nil :ascent center)
|
|
("." ,(list (all-the-icons-faicon "circle-o")) nil nil :ascent center))))
|
|
#+end_src
|
|
** Export
|
|
*** Hugo
|
|
#+begin_src emacs-lisp
|
|
(use-package ox-hugo
|
|
:straight t
|
|
:after ox)
|
|
#+end_src
|
|
*** Jupyter Notebook
|
|
#+begin_src emacs-lisp
|
|
(use-package ox-ipynb
|
|
:straight (:host github :repo "jkitchin/ox-ipynb")
|
|
:after ox)
|
|
#+end_src
|
|
*** Html export
|
|
#+begin_src emacs-lisp
|
|
(use-package htmlize
|
|
:straight t
|
|
:after ox
|
|
:config
|
|
(setq org-html-htmlize-output-type 'css))
|
|
#+end_src
|
|
*** LaTeX
|
|
Add a custom LaTeX template without default packages. Packages are indented to be imported with function from [[Import *.sty]].
|
|
#+begin_src emacs-lisp
|
|
(defun my/setup-org-latex ()
|
|
(setq org-latex-compiler "xelatex") ;; Probably not necessary
|
|
(setq org-latex-pdf-process '("latexmk -outdir=%o %f")) ;; Use latexmk
|
|
(setq org-latex-listings 'minted) ;; Use minted to highlight source code
|
|
(setq org-latex-minted-options ;; Some minted options I like
|
|
'(("breaklines" "true")
|
|
("tabsize" "4")
|
|
("autogobble")
|
|
("linenos")
|
|
("numbersep" "0.5cm")
|
|
("xleftmargin" "1cm")
|
|
("frame" "single")))
|
|
;; Use extarticle without the default packages
|
|
(add-to-list 'org-latex-classes
|
|
'("org-plain-extarticle"
|
|
"\\documentclass{extarticle}
|
|
[NO-DEFAULT-PACKAGES]
|
|
[PACKAGES]
|
|
[EXTRA]"
|
|
("\\section{%s}" . "\\section*{%s}")
|
|
("\\subsection{%s}" . "\\subsection*{%s}")
|
|
("\\subsubsection{%s}" . "\\subsubsection*{%s}")
|
|
("\\paragraph{%s}" . "\\paragraph*{%s}")
|
|
("\\subparagraph{%s}" . "\\subparagraph*{%s}")))
|
|
;; Use beamer without the default packages
|
|
(add-to-list 'org-latex-classes
|
|
'("org-latex-beamer"
|
|
"\\documentclass{beamer}
|
|
[NO-DEFAULT-PACKAGES]
|
|
[PACKAGES]
|
|
[EXTRA]"
|
|
("beamer" "\\documentclass[presentation]{beamer}"
|
|
("\\section{%s}" . "\\section*{%s}")
|
|
("\\subsection{%s}" . "\\subsection*{%s}")
|
|
("\\subsubsection{%s}" . "\\subsubsection*{%s}")))))
|
|
|
|
;; Make sure to eval the function when org-latex-classes list already exists
|
|
(with-eval-after-load 'ox-latex
|
|
(my/setup-org-latex))
|
|
#+end_src
|
|
** Keybindings & stuff
|
|
#+begin_src emacs-lisp :tangle no :noweb-ref org-keys-setup
|
|
(general-define-key
|
|
:keymaps 'org-mode-map
|
|
"C-c d" 'org-decrypt-entry
|
|
"C-c e" 'org-encrypt-entry
|
|
"M-p" 'org-latex-preview
|
|
"M-o" 'org-redisplay-inline-images)
|
|
|
|
(general-define-key
|
|
:keymaps 'org-mode-map
|
|
:states '(normal emacs)
|
|
"L" 'org-shiftright
|
|
"H" 'org-shiftleft
|
|
"S-<next>" 'org-next-visible-heading
|
|
"S-<prior>" 'org-previous-visible-heading
|
|
"M-0" 'org-next-visible-heading
|
|
"M-9" 'org-previous-visible-heading
|
|
"M-]" 'org-babel-next-src-block
|
|
"M-[" 'org-babel-previous-src-block)
|
|
|
|
(general-define-key
|
|
:keymaps 'org-agenda-mode-map
|
|
"M-]" 'org-agenda-later
|
|
"M-[" 'org-agenda-earlier)
|
|
|
|
;; (general-imap :keymaps 'org-mode-map "RET" 'evil-org-return)
|
|
(general-nmap :keymaps 'org-mode-map "RET" 'org-ctrl-c-ctrl-c)
|
|
|
|
(my-leader-def "aa" 'org-agenda)
|
|
#+end_src
|
|
*** Copy a link
|
|
#+begin_src emacs-lisp :noweb-ref org-keys-setup
|
|
(defun my/org-link-copy (&optional arg)
|
|
"Extract URL from org-mode link and add it to kill ring."
|
|
(interactive "P")
|
|
(let* ((link (org-element-lineage (org-element-context) '(link) t))
|
|
(type (org-element-property :type link))
|
|
(url (org-element-property :path link))
|
|
(url (concat type ":" url)))
|
|
(kill-new url)
|
|
(message (concat "Copied URL: " url))))
|
|
|
|
(general-nmap :keymaps 'org-mode-map
|
|
"C-x C-l" 'my/org-link-copy)
|
|
#+end_src
|
|
** Presentations
|
|
Doing presentations with [[https://github.com/rlister/org-present][org-present]].
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package hide-mode-line
|
|
:straight t
|
|
:after (org-present))
|
|
|
|
(defun my/present-next-with-latex ()
|
|
(interactive)
|
|
(org-present-next)
|
|
(org-latex-preview '(16)))
|
|
|
|
(defun my/present-prev-with-latex ()
|
|
(interactive)
|
|
(org-present-prev)
|
|
(org-latex-preview '(16)))
|
|
|
|
(use-package org-present
|
|
:straight (:host github :repo "rlister/org-present")
|
|
:commands (org-present)
|
|
:config
|
|
(general-define-key
|
|
:keymaps 'org-present-mode-keymap
|
|
"<next>" 'my/present-next-with-latex
|
|
"<prior>" 'my/present-prev-with-latex)
|
|
(add-hook 'org-present-mode-hook
|
|
(lambda ()
|
|
(blink-cursor-mode 0)
|
|
(org-present-big)
|
|
;; (org-display-inline-images)
|
|
(org-present-hide-cursor)
|
|
(org-present-read-only)
|
|
(display-line-numbers-mode 0)
|
|
(hide-mode-line-mode +1)
|
|
(setq-local org-format-latex-options
|
|
(plist-put org-format-latex-options
|
|
:scale (* org-present-text-scale my/org-latex-scale 0.5)))
|
|
(org-latex-preview '(16))
|
|
(tab-bar-mode 0)))
|
|
(add-hook 'org-present-mode-quit-hook
|
|
(lambda ()
|
|
(blink-cursor-mode 1)
|
|
(org-present-small)
|
|
;; (org-remove-inline-images)
|
|
(org-present-show-cursor)
|
|
(org-present-read-write)
|
|
(display-line-numbers-mode 1)
|
|
(hide-mode-line-mode 0)
|
|
(setq-local org-format-latex-options (plist-put org-format-latex-options :scale my/org-latex-scale))
|
|
(org-latex-preview '(64))
|
|
(tab-bar-mode 1))))
|
|
#+end_src
|
|
** TOC
|
|
Make a TOC inside the org file.
|
|
|
|
References:
|
|
- [[https://github.com/alphapapa/org-make-toc][alphapapa/org-make-toc]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package org-make-toc
|
|
:after (org)
|
|
:commands
|
|
(org-make-toc
|
|
org-make-toc-insert
|
|
org-make-toc-set
|
|
org-make-toc-at-point)
|
|
:straight t)
|
|
#+end_src
|
|
** System configuration
|
|
Functions used across my literate config files.
|
|
|
|
*** Tables for Guix Dependencies
|
|
A function to extract Guix dependencies from the org file.
|
|
|
|
- If column name matches =[G|g]uix.*dep=, its contents will be added to the result.
|
|
- If =CATEGORY= is passed, a column with name =[C|c]ategory= will be used to filter results. That way one file can be used to produce multiple manifests.
|
|
- If =CATEGORY= is not passed, entries with non-empty category will be filtered out
|
|
- If there is a =[D|d]isabled= column, entries which have non-empty value in this column will be filtered out.
|
|
|
|
#+begin_src emacs-lisp :noweb-ref guix-tables
|
|
(defun my/extract-guix-dependencies (&optional category)
|
|
(let ((dependencies '()))
|
|
(org-table-map-tables
|
|
(lambda ()
|
|
(let* ((table
|
|
(seq-filter
|
|
(lambda (q) (not (eq q 'hline)))
|
|
(org-table-to-lisp)))
|
|
(dep-name-index
|
|
(cl-position
|
|
nil
|
|
(mapcar #'substring-no-properties (nth 0 table))
|
|
:test (lambda (_ elem)
|
|
(string-match-p "[G|g]uix.*dep" elem))))
|
|
(category-name-index
|
|
(cl-position
|
|
nil
|
|
(mapcar #'substring-no-properties (nth 0 table))
|
|
:test (lambda (_ elem)
|
|
(string-match-p ".*[C|c]ategory.*" elem))))
|
|
(disabled-name-index
|
|
(cl-position
|
|
nil
|
|
(mapcar #'substring-no-properties (nth 0 table))
|
|
:test (lambda (_ elem)
|
|
(string-match-p ".*[D|d]isabled.*" elem)))))
|
|
(when dep-name-index
|
|
(dolist (elem (cdr table))
|
|
(when
|
|
(and
|
|
;; Category
|
|
(or
|
|
;; Category not set and not present in the table
|
|
(and
|
|
(or (not category) (string-empty-p category))
|
|
(not category-name-index))
|
|
;; Category is set and present in the table
|
|
(and
|
|
category-name-index
|
|
(not (string-empty-p category))
|
|
(string-match-p category (nth category-name-index elem))))
|
|
;; Not disabled
|
|
(or
|
|
(not disabled-name-index)
|
|
(string-empty-p (nth disabled-name-index elem))))
|
|
(add-to-list
|
|
'dependencies
|
|
(substring-no-properties (nth dep-name-index elem)))))))))
|
|
dependencies))
|
|
#+end_src
|
|
|
|
Now, join dependencies list to make it compatible with Scheme:
|
|
#+begin_src emacs-lisp :noweb-ref guix-tables
|
|
(defun my/format-guix-dependencies (&optional category)
|
|
(mapconcat
|
|
(lambda (e) (concat "\"" e "\""))
|
|
(my/extract-guix-dependencies category)
|
|
"\n"))
|
|
#+end_src
|
|
|
|
*** Noweb evaluations
|
|
Turn off eval confirmations for configuration files.
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq my/org-config-files
|
|
'("/home/pavel/Emacs.org"
|
|
"/home/pavel/Desktop.org"
|
|
"/home/pavel/Console.org"
|
|
"/home/pavel/Guix.org"
|
|
"/home/pavel/Mail.org"))
|
|
|
|
(add-hook 'org-mode-hook
|
|
(lambda ()
|
|
(when (member (buffer-file-name) my/org-config-files)
|
|
(setq-local org-confirm-babel-evaluate nil))))
|
|
#+end_src
|
|
*** yadm hook
|
|
A script to run tangle from CLI.
|
|
|
|
#+begin_src emacs-lisp :tangle ~/.config/yadm/hooks/run-tangle.el :noweb yes
|
|
(require 'org)
|
|
|
|
(org-babel-do-load-languages
|
|
'org-babel-load-languages
|
|
'((emacs-lisp . t)
|
|
(shell . t)))
|
|
|
|
;; Do not ask to confirm evaluations
|
|
(setq org-confirm-babel-evaluate nil)
|
|
|
|
<<guix-tables>>
|
|
|
|
;; A few dummy modes to avoid being prompted for comment systax
|
|
(define-derived-mode fish-mode prog-mode "Fish"
|
|
(setq-local comment-start "# ")
|
|
(setq-local comment-start-skip "#+[\t ]*"))
|
|
|
|
(define-derived-mode yaml-mode text-mode "YAML"
|
|
(setq-local comment-start "# ")
|
|
(setq-local comment-start-skip "#+ *"))
|
|
|
|
(mapcar #'org-babel-tangle-file
|
|
'("/home/pavel/Emacs.org"
|
|
"/home/pavel/Desktop.org"
|
|
"/home/pavel/Console.org"
|
|
"/home/pavel/Guix.org"
|
|
"/home/pavel/Mail.org"))
|
|
#+end_src
|
|
|
|
To launch from CLI, run:
|
|
#+begin_src bash :tangle no
|
|
emacs -Q --batch -l run-tangle.el
|
|
#+end_src
|
|
|
|
I have added this line to yadm's =post_alt= hook, so tangle is ran after =yadm alt=
|
|
* OFF (OFF) EAF
|
|
[[https://github.com/manateelazycat/emacs-application-framework][Emacs Application Framework]] provides a way to integrate PyQt applications with Emacs.
|
|
|
|
I've made it work, but don't find any uses cases for me at the moment
|
|
** Installation
|
|
Requirements: Node >= 14
|
|
#+begin_src bash :tangle no
|
|
pip install qtconsole markdown qrcode[pil] PyQt5 PyQtWebEngine
|
|
#+end_src
|
|
** Config
|
|
#+begin_src emacs-lisp :tangle no
|
|
(use-package eaf
|
|
:straight (:host github :repo "manateelazycat/emacs-application-framework" :files ("*"))
|
|
:init
|
|
(use-package epc :defer t :straight t)
|
|
(use-package ctable :defer t :straight t)
|
|
(use-package deferred :defer t :straight t)
|
|
:config
|
|
(require 'eaf-evil)
|
|
(setq eaf-evil-leader-key "SPC"))
|
|
#+end_src
|
|
* Programming
|
|
** General setup
|
|
*** LSP
|
|
LSP-mode provides an IDE-like experience for Emacs - real-time diagnostic, code actions, intelligent autocompletion, etc.
|
|
|
|
References:
|
|
- [[https://emacs-lsp.github.io/lsp-mode/][lsp-mode homepage]]
|
|
**** Setup
|
|
#+begin_src emacs-lisp
|
|
(use-package lsp-mode
|
|
:straight t
|
|
:if (not my/slow-ssh)
|
|
:hook (
|
|
(typescript-mode . lsp)
|
|
(vue-mode . lsp)
|
|
(go-mode . lsp)
|
|
(svelte-mode . lsp)
|
|
;; (python-mode . lsp)
|
|
(json-mode . lsp)
|
|
(haskell-mode . lsp)
|
|
(haskell-literate-mode . lsp)
|
|
(java-mode . lsp)
|
|
(csharp-mode . lsp))
|
|
:commands lsp
|
|
:config
|
|
(setq lsp-idle-delay 1)
|
|
(setq lsp-eslint-server-command '("node" "/home/pavel/.emacs.d/.cache/lsp/eslint/unzipped/extension/server/out/eslintServer.js" "--stdio"))
|
|
(setq lsp-eslint-run "onSave")
|
|
(setq lsp-signature-render-documentation nil)
|
|
;; (lsp-headerline-breadcrumb-mode nil)
|
|
(setq lsp-headerline-breadcrumb-enable nil)
|
|
(add-to-list 'lsp-language-id-configuration '(svelte-mode . "svelte")))
|
|
|
|
(use-package lsp-ui
|
|
:straight t
|
|
:commands lsp-ui-mode
|
|
:config
|
|
(setq lsp-ui-doc-delay 2)
|
|
(setq lsp-ui-sideline-show-hover nil))
|
|
#+end_src
|
|
**** Integrations
|
|
The only integration left now is treemacs.
|
|
|
|
Origami should've leveraged LSP folding, but it was too unstable at the moment I tried it.
|
|
#+begin_src emacs-lisp
|
|
;; (use-package helm-lsp
|
|
;; :straight t
|
|
;; :commands helm-lsp-workspace-symbol)
|
|
|
|
;; (use-package origami
|
|
;; :straight t
|
|
;; :hook (prog-mode . origami-mode))
|
|
|
|
;; (use-package lsp-origami
|
|
;; :straight t
|
|
;; :config
|
|
;; (add-hook 'lsp-after-open-hook #'lsp-origami-try-enable))
|
|
|
|
(use-package lsp-treemacs
|
|
:straight t
|
|
:commands lsp-treemacs-errors-list)
|
|
#+end_src
|
|
**** Keybindings
|
|
#+begin_src emacs-lisp
|
|
(my-leader-def
|
|
"ld" 'lsp-ui-peek-find-definitions
|
|
"lr" 'lsp-rename
|
|
"lu" 'lsp-ui-peek-find-references
|
|
"ls" 'lsp-ui-find-workspace-symbol
|
|
;; "la" 'helm-lsp-code-actions
|
|
"le" 'list-flycheck-errors)
|
|
#+end_src
|
|
*** Flycheck
|
|
A syntax checking extension for Emacs. Integrates with LSP-mode, but can also use various standalone checkers.
|
|
|
|
References:
|
|
- [[https://www.flycheck.org/en/latest/][Flycheck homepage]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package flycheck
|
|
:straight t
|
|
:config
|
|
(global-flycheck-mode)
|
|
(setq flycheck-check-syntax-automatically '(save idle-buffer-switch mode-enabled))
|
|
;; (add-hook 'evil-insert-state-exit-hook
|
|
;; (lambda ()
|
|
;; (if flycheck-checker
|
|
;; (flycheck-buffer))
|
|
;; ))
|
|
(advice-add 'flycheck-eslint-config-exists-p :override (lambda() t))
|
|
(add-to-list 'display-buffer-alist
|
|
`(,(rx bos "*Flycheck errors*" eos)
|
|
(display-buffer-reuse-window
|
|
display-buffer-in-side-window)
|
|
(side . bottom)
|
|
(reusable-frames . visible)
|
|
(window-height . 0.33))))
|
|
#+end_src
|
|
*** Tree Sitter
|
|
An incremental code parsing system, constructing a syntax tree at runtime.
|
|
|
|
Right now it doesn't do much expect providing a better syntax highlighting than regexes, but this integration is a rather recent development. There are already some major modes built on top of this thing.
|
|
|
|
Also, it seems to break if ran from mmm-mode, so there is a small workaround.
|
|
|
|
References:
|
|
- [[https://tree-sitter.github.io/tree-sitter/][Tree-sitter library]]
|
|
- [[https://ubolonton.github.io/emacs-tree-sitter/][Emacs Tree-sitter]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(defun my/tree-sitter-if-not-mmm ()
|
|
(when (not (and (boundp 'mmm-temp-buffer-name)
|
|
(string-equal mmm-temp-buffer-name (buffer-name))))
|
|
(tree-sitter-mode)
|
|
(tree-sitter-hl-mode)))
|
|
|
|
(use-package tree-sitter
|
|
:straight t
|
|
:hook ((typescript-mode . my/tree-sitter-if-not-mmm)
|
|
(js-mode . my/tree-sitter-if-not-mmm)
|
|
(python-mode . tree-sitter-mode)
|
|
(python-mode . tree-sitter-hl-mode)
|
|
(csharp-mode . tree-sitter-mode)))
|
|
|
|
(use-package tree-sitter-langs
|
|
:straight t
|
|
:after tree-sitter)
|
|
#+end_src
|
|
*** OFF (OFF) DAP
|
|
An Emacs client for Debugger Adapter Protocol.
|
|
|
|
I don't use it now, because there are debuggers I like more for the technologies I'm currently using.
|
|
|
|
References:
|
|
- [[https://emacs-lsp.github.io/dap-mode/][dap-mode homepage]]
|
|
#+begin_src emacs-lisp :tangle no
|
|
(use-package dap-mode
|
|
:straight t
|
|
:defer t
|
|
:init
|
|
(setq lsp-enable-dap-auto-configure nil)
|
|
:config
|
|
|
|
(setq dap-ui-variable-length 100)
|
|
(require 'dap-node)
|
|
(dap-node-setup)
|
|
|
|
(require 'dap-chrome)
|
|
(dap-chrome-setup)
|
|
|
|
(require 'dap-python)
|
|
|
|
(dap-mode 1)
|
|
(dap-ui-mode 1)
|
|
(dap-tooltip-mode 1)
|
|
(tooltip-mode 1)
|
|
(dap-ui-controls-mode 1))
|
|
|
|
(my-leader-def
|
|
:infix "d"
|
|
"d" 'dap-debug
|
|
"b" 'dap-breakpoint-toggle
|
|
"c" 'dap-breakpoint-condition
|
|
"wl" 'dap-ui-locals
|
|
"wb" 'dap-ui-breakpoints
|
|
"wr" 'dap-ui-repl
|
|
"ws" 'dap-ui-sessions
|
|
"we" 'dap-ui-expressions)
|
|
|
|
(my-leader-def
|
|
:infix "d"
|
|
:keymaps 'dap-mode-map
|
|
"h" 'dap-hydra)
|
|
|
|
(defun my/dap-yank-value-at-point (node)
|
|
(interactive (list (treemacs-node-at-point)))
|
|
(kill-new (message (plist-get (button-get node :item) :value))))
|
|
#+end_src
|
|
*** OFF (OFF) TabNine
|
|
A ML-based autocompletion system.
|
|
|
|
More often than not gives really good results, but slow as hell & consumes a lot of RAM. Also, LSP-provided completions were more useful in my experience.
|
|
|
|
References:
|
|
- [[https://www.tabnine.com/][TabNine Homepage]]
|
|
#+begin_src emacs-lisp :tangle no
|
|
(use-package company-tabnine
|
|
:straight t
|
|
:if (not my/lowpower)
|
|
:after company
|
|
:config
|
|
(add-to-list 'company-backends #'company-tabnine))
|
|
#+end_src
|
|
*** OFF (OFF) Code Compass
|
|
A set of code analysing tools.
|
|
|
|
References:
|
|
- [[https://github.com/ag91/code-compass][code-compass repo]]
|
|
|
|
**** Dependencies
|
|
#+begin_src emacs-lisp :tangle no
|
|
(use-package async
|
|
:straight t)
|
|
(use-package dash
|
|
:straight t)
|
|
(use-package f
|
|
:straight t)
|
|
(use-package s
|
|
:straight t)
|
|
(use-package simple-httpd
|
|
:straight t)
|
|
#+end_src
|
|
**** Plugin
|
|
#+begin_src emacs-lisp :tangle no
|
|
(use-package code-compass
|
|
:straight (
|
|
:repo "ag91/code-compass"
|
|
:files ("code-compass.el")
|
|
:branch "main"
|
|
))
|
|
#+end_src
|
|
*** CHECK (OFF) Format-all
|
|
#+begin_src emacs-lisp :tangle no
|
|
(use-package format-all
|
|
:straight t)
|
|
#+end_src
|
|
*** General additional config
|
|
Make smartparens behave the way I like for C-like languages.
|
|
#+begin_src emacs-lisp
|
|
(defun my/set-smartparens-indent (mode)
|
|
(sp-local-pair mode "{" nil :post-handlers '(("|| " "SPC") ("||\n[i]" "RET")))
|
|
(sp-local-pair mode "[" nil :post-handlers '(("|| " "SPC") ("||\n[i]" "RET")))
|
|
(sp-local-pair mode "(" nil :post-handlers '(("|| " "SPC") ("||\n[i]" "RET"))))
|
|
#+end_src
|
|
|
|
Override flycheck checker with eslint.
|
|
#+begin_src emacs-lisp
|
|
(defun my/set-flycheck-eslint()
|
|
"Override flycheck checker with eslint."
|
|
(setq-local lsp-diagnostic-package :none)
|
|
(setq-local flycheck-checker 'javascript-eslint))
|
|
#+end_src
|
|
** Web development
|
|
Configs for various web development technologies I'm using.
|
|
*** Emmet
|
|
[[https://emmet.io/][Emmet]] is a toolkit which greatly speeds up typing HTML & CSS.
|
|
|
|
| Type | Note |
|
|
|------+---------------------------------------------------|
|
|
| TODO | Do not enable for every Svelte mode |
|
|
| TODO | make expand div[disabled] as <div disabled></div> |
|
|
|
|
My bit of config here:
|
|
- makes Emmet activate only in certain mmm-mode submodes.
|
|
- makes =TAB= the only key I have to use
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package emmet-mode
|
|
:straight t
|
|
:hook ((vue-html-mode . emmet-mode)
|
|
(svelte-mode . emmet-mode)
|
|
(html-mode . emmet-mode)
|
|
(css-mode . emmet-mode)
|
|
(scss-mode . emmet-mode))
|
|
:config
|
|
;; (setq emmet-indent-after-insert nil)
|
|
(setq my/emmet-mmm-submodes '(vue-html-mode css-mode))
|
|
(defun my/emmet-or-tab (&optional arg)
|
|
(interactive)
|
|
(if (and
|
|
(boundp 'mmm-current-submode)
|
|
mmm-current-submode
|
|
(not (member mmm-current-submode my/emmet-mmm-submodes)))
|
|
(indent-for-tab-command arg)
|
|
(or (emmet-expand-line arg)
|
|
(emmet-go-to-edit-point 1)
|
|
(indent-for-tab-command arg))))
|
|
(general-imap :keymaps 'emmet-mode-keymap
|
|
"TAB" 'my/emmet-or-tab
|
|
"<backtab>" 'emmet-prev-edit-point))
|
|
#+end_src
|
|
*** Prettier
|
|
#+begin_src emacs-lisp
|
|
(use-package prettier
|
|
:commands (prettier-prettify)
|
|
:straight t
|
|
:init
|
|
(my-leader-def
|
|
:keymaps '(js-mode-map typescript-mode-map vue-mode-map svelte-mode-map)
|
|
"rr" #'prettier-prettify))
|
|
#+end_src
|
|
*** TypeScript
|
|
#+begin_src emacs-lisp
|
|
(use-package typescript-mode
|
|
:straight t
|
|
:mode "\\.ts\\'"
|
|
:config
|
|
(add-hook 'typescript-mode-hook #'smartparens-mode)
|
|
(add-hook 'typescript-mode-hook #'rainbow-delimiters-mode)
|
|
(add-hook 'typescript-mode-hook #'hs-minor-mode)
|
|
(my/set-smartparens-indent 'typescript-mode))
|
|
#+end_src
|
|
*** JavaScript
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'js-mode-hook #'smartparens-mode)
|
|
(add-hook 'js-mode-hook #'hs-minor-mode)
|
|
(my/set-smartparens-indent 'js-mode)
|
|
#+end_src
|
|
*** Jest
|
|
#+begin_src emacs-lisp
|
|
(use-package jest-test-mode
|
|
:straight t
|
|
:hook ((typescript-mode . jest-test-mode)
|
|
(js-mode . jest-test-mode))
|
|
:config
|
|
(my-leader-def
|
|
:keymaps 'jest-test-mode-map
|
|
:infix "t"
|
|
"t" 'jest-test-run-at-point
|
|
"r" 'jest-test-run
|
|
"a" 'jest-test-run-all-tests))
|
|
#+end_src
|
|
*** Vue.js
|
|
#+begin_src emacs-lisp :noweb yes
|
|
(use-package vue-mode
|
|
:straight t
|
|
:mode "\\.vue\\'"
|
|
:config
|
|
(add-hook 'vue-mode-hook #'hs-minor-mode)
|
|
(add-hook 'vue-mode-hook #'smartparens-mode)
|
|
(my/set-smartparens-indent 'vue-mode)
|
|
(add-hook 'vue-mode-hook (lambda () (set-face-background 'mmm-default-submode-face nil)))
|
|
<<override-mmm-mode-func>>)
|
|
|
|
(with-eval-after-load 'editorconfig
|
|
(add-to-list 'editorconfig-indentation-alist
|
|
'(vue-mode css-indent-offset
|
|
js-indent-level
|
|
sgml-basic-offset
|
|
ssass-tab-width
|
|
typescript-indent-level
|
|
emmet-indentation
|
|
vue-html-extra-indent)))
|
|
#+end_src
|
|
**** mmm-mode fix
|
|
References:
|
|
- [[https://github.com/purcell/mmm-mode/issues/112][mmm-mode issue]]
|
|
|
|
#+begin_src emacs-lisp :noweb-ref override-mmm-mode-func :tangle no
|
|
(defun mmm-syntax-propertize-function (start stop)
|
|
(let ((saved-mode mmm-current-submode)
|
|
(saved-ovl mmm-current-overlay))
|
|
(mmm-save-changed-local-variables
|
|
mmm-current-submode mmm-current-overlay)
|
|
(unwind-protect
|
|
(mapc (lambda (elt)
|
|
(let* ((mode (car elt))
|
|
(func (get mode 'mmm-syntax-propertize-function))
|
|
(beg (cadr elt)) (end (nth 2 elt))
|
|
(ovl (nth 3 elt))
|
|
syntax-ppss-cache
|
|
syntax-ppss-last)
|
|
(goto-char beg)
|
|
(mmm-set-current-pair mode ovl)
|
|
(mmm-set-local-variables mode mmm-current-overlay)
|
|
(save-restriction
|
|
(if mmm-current-overlay
|
|
(narrow-to-region (overlay-start mmm-current-overlay)
|
|
(overlay-end mmm-current-overlay))
|
|
(narrow-to-region beg end))
|
|
(cond
|
|
(func
|
|
(funcall func beg end))
|
|
(font-lock-syntactic-keywords
|
|
(let ((syntax-propertize-function nil))
|
|
(font-lock-fontify-syntactic-keywords-region beg end))))
|
|
(run-hook-with-args 'mmm-after-syntax-propertize-functions
|
|
mmm-current-overlay mode beg end))))
|
|
(mmm-regions-in start stop))
|
|
(mmm-set-current-pair saved-mode saved-ovl)
|
|
(mmm-set-local-variables (or saved-mode mmm-primary-mode) saved-ovl))))
|
|
#+end_src
|
|
*** Svelte
|
|
#+begin_src emacs-lisp
|
|
(use-package svelte-mode
|
|
:straight t
|
|
:mode "\\.svelte\\'"
|
|
:config
|
|
(add-hook 'svelte-mode-hook 'my/set-flycheck-eslint)
|
|
(add-hook 'svelte-mode-hook #'smartparens-mode)
|
|
(my/set-smartparens-indent 'svelte-mode)
|
|
;; I have my own Emmet
|
|
(setq lsp-svelte-plugin-css-completions-emmet nil)
|
|
(setq lsp-svelte-plugin-html-completions-emmet nil))
|
|
#+end_src
|
|
*** SCSS
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'scss-mode-hook #'smartparens-mode)
|
|
(add-hook 'scss-mode-hook #'hs-minor-mode)
|
|
(my/set-smartparens-indent 'scss-mode)
|
|
#+end_src
|
|
*** PHP
|
|
#+begin_src emacs-lisp
|
|
(use-package php-mode
|
|
:straight t
|
|
:mode "\\.php\\'")
|
|
#+end_src
|
|
** LaTeX
|
|
*** AUCTeX
|
|
The best LaTeX editing environment I've found so far.
|
|
|
|
References:
|
|
- [[https://www.gnu.org/software/auctex/][AUCTeX homepage]]
|
|
|
|
#+begin_src emacs-lisp :noweb yes
|
|
(use-package tex
|
|
:straight auctex
|
|
:defer t
|
|
:config
|
|
(setq-default TeX-auto-save t)
|
|
(setq-default TeX-parse-self t)
|
|
(TeX-PDF-mode)
|
|
;; Use XeLaTeX & stuff
|
|
(setq-default TeX-engine 'xetex)
|
|
(setq-default TeX-command-extra-options "-shell-escape")
|
|
(setq-default TeX-source-correlate-method 'synctex)
|
|
(TeX-source-correlate-mode)
|
|
(setq-default TeX-source-correlate-start-server t)
|
|
(setq-default LaTeX-math-menu-unicode t)
|
|
|
|
(setq-default font-latex-fontify-sectioning 1.3)
|
|
|
|
;; Scale preview for my DPI
|
|
(setq-default preview-scale-function 1.4)
|
|
(when (boundp 'tex--prettify-symbols-alist)
|
|
(assoc-delete-all "--" tex--prettify-symbols-alist)
|
|
(assoc-delete-all "---" tex--prettify-symbols-alist))
|
|
|
|
(add-hook 'LaTeX-mode-hook
|
|
(lambda ()
|
|
(TeX-fold-mode 1)
|
|
(outline-minor-mode)))
|
|
|
|
(add-to-list 'TeX-view-program-selection
|
|
'(output-pdf "Zathura"))
|
|
|
|
;; Do not run lsp within templated TeX files
|
|
(add-hook 'LaTeX-mode-hook
|
|
(lambda ()
|
|
(unless (string-match "\.hogan\.tex$" (buffer-name))
|
|
(lsp))
|
|
(setq-local lsp-diagnostic-package :none)
|
|
(setq-local flycheck-checker 'tex-chktex)))
|
|
|
|
(add-hook 'LaTeX-mode-hook #'rainbow-delimiters-mode)
|
|
(add-hook 'LaTeX-mode-hook #'smartparens-mode)
|
|
(add-hook 'LaTeX-mode-hook #'prettify-symbols-mode)
|
|
|
|
(my/set-smartparens-indent 'LaTeX-mode)
|
|
(require 'smartparens-latex)
|
|
|
|
(general-nmap
|
|
:keymaps '(LaTeX-mode-map latex-mode-map)
|
|
"RET" 'TeX-command-run-all
|
|
"C-c t" 'orgtbl-mode)
|
|
|
|
<<init-greek-latex-snippets>>
|
|
<<init-english-latex-snippets>>
|
|
<<init-math-latex-snippets>>
|
|
<<init-section-latex-snippets>>)
|
|
#+end_src
|
|
*** BibTeX
|
|
#+begin_src emacs-lisp
|
|
(use-package ivy-bibtex
|
|
:commands (ivy-bibtex)
|
|
:straight t
|
|
:init
|
|
(my-leader-def "fB" 'ivy-bibtex))
|
|
|
|
(add-hook 'bibtex-mode 'smartparens-mode)
|
|
#+end_src
|
|
*** Import *.sty
|
|
A function to import =.sty= files to the LaTeX document.
|
|
|
|
#+begin_src emacs-lisp
|
|
(defun my/list-sty ()
|
|
(reverse
|
|
(sort
|
|
(seq-filter
|
|
(lambda (file) (if (string-match ".*\.sty$" file) 1 nil))
|
|
(directory-files
|
|
(seq-some
|
|
(lambda (dir)
|
|
(if (and
|
|
(f-directory-p dir)
|
|
(seq-some
|
|
(lambda (file) (string-match ".*\.sty$" file))
|
|
(directory-files dir))
|
|
) dir nil))
|
|
(list "./styles" "../styles/" "." "..")) :full))
|
|
(lambda (f1 f2)
|
|
(let ((f1b (file-name-base f1))
|
|
(f1b (file-name-base f2)))
|
|
(cond
|
|
((string-match-p ".*BibTex" f1) t)
|
|
((and (string-match-p ".*Locale" f1) (not (string-match-p ".*BibTex" f2))) t)
|
|
((string-match-p ".*Preamble" f2) t)
|
|
(t (string-lessp f1 f2))))))))
|
|
|
|
(defun my/import-sty ()
|
|
(interactive)
|
|
(insert
|
|
(apply #'concat
|
|
(cl-mapcar
|
|
(lambda (file) (concat "\\usepackage{" (file-name-sans-extension (file-relative-name file default-directory)) "}\n"))
|
|
(my/list-sty)))))
|
|
|
|
(defun my/import-sty-org ()
|
|
(interactive)
|
|
(insert
|
|
(apply #'concat
|
|
(cl-mapcar
|
|
(lambda (file) (concat "#+LATEX_HEADER: \\usepackage{" (file-name-sans-extension (file-relative-name file default-directory)) "}\n"))
|
|
(my/list-sty)))))
|
|
#+end_src
|
|
*** Snippets
|
|
| Note | Type |
|
|
|------+-----------------------------------------------------------------|
|
|
| TODO | Move yasnippet snippets here? Maybe extract to a separate file? |
|
|
**** Greek letters
|
|
Autogenerate snippets for greek letters. I have a few blocks like this because it's faster & more flexible than usual yasnippet snippets.
|
|
|
|
Noweb points to the AUCTeX config block.
|
|
|
|
#+begin_src emacs-lisp :noweb-ref init-greek-latex-snippets
|
|
(setq my/greek-alphabet
|
|
'(("a" . "\\alpha")
|
|
("b" . "\\beta" )
|
|
("g" . "\\gamma")
|
|
("d" . "\\delta")
|
|
("e" . "\\epsilon")
|
|
("z" . "\\zeta")
|
|
("h" . "\\eta")
|
|
("o" . "\\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 my/latex-greek-prefix "'")
|
|
|
|
;; The same for capitalized letters
|
|
(dolist (elem my/greek-alphabet)
|
|
(let ((key (car elem))
|
|
(value (cdr elem)))
|
|
(when (string-equal key (downcase key))
|
|
(add-to-list 'my/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 my/latex-greek-prefix (car elem)) (cdr elem) (concat "Greek letter " (car elem))))
|
|
my/greek-alphabet))
|
|
#+end_src
|
|
**** English letters
|
|
#+begin_src emacs-lisp :noweb-ref init-english-latex-snippets
|
|
(setq my/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 my/english-alphabet)
|
|
(when (string-equal elem (downcase elem))
|
|
(add-to-list 'my/english-alphabet (upcase elem))))
|
|
|
|
(setq my/latex-mathbb-prefix "`")
|
|
|
|
(yas-define-snippets
|
|
'latex-mode
|
|
(mapcar
|
|
(lambda (elem)
|
|
(list (concat my/latex-mathbb-prefix elem) (concat "\\mathbb{" elem "}") (concat "Mathbb letter " elem)))
|
|
my/english-alphabet))
|
|
#+end_src
|
|
**** Math symbols
|
|
#+begin_src emacs-lisp :noweb-ref init-math-latex-snippets
|
|
(setq my/latex-math-symbols
|
|
'(("x" . "\\times")
|
|
("." . "\\cdot")
|
|
("v" . "\\forall")
|
|
("s" . "\\sum_{$1}^{$2}$0")
|
|
("p" . "\\prod_{$1}^{$2}$0")
|
|
("d" . "\\partial")
|
|
("e" . "\\exists")
|
|
("i" . "\\int_{$1}^{$2}$0")
|
|
("c" . "\\cap")
|
|
("u" . "\\cup")
|
|
("0" . "\\emptyset")
|
|
("^" . "\\widehat{$1}$0")
|
|
("_" . "\\overline{$1}$0")
|
|
("~" . "\\sim")
|
|
("|" . "\\mid")
|
|
("_|" . "\\perp")))
|
|
|
|
(setq my/latex-math-prefix ";")
|
|
|
|
(yas-define-snippets
|
|
'latex-mode
|
|
(mapcar
|
|
(lambda (elem)
|
|
(let ((key (car elem))
|
|
(value (cdr elem)))
|
|
(list (concat my/latex-math-prefix key) value (concat "Math symbol " value))))
|
|
my/latex-math-symbols))
|
|
#+end_src
|
|
**** Section snippets
|
|
Section snippets. The code turned out to be more complicated than just writing the snippets by hand.
|
|
|
|
#+begin_src emacs-lisp :noweb-ref init-section-latex-snippets
|
|
(setq my/latex-section-snippets
|
|
'(("ch" . "\\chapter{$1}")
|
|
("sec" . "\\section{$1}")
|
|
("ssec" . "\\subsection{$1}")
|
|
("sssec" . "\\subsubsection{$1}")
|
|
("par" . "\\paragraph{$1}}")))
|
|
|
|
(setq my/latex-section-snippets
|
|
(mapcar
|
|
(lambda (elem)
|
|
`(,(car elem)
|
|
,(cdr elem)
|
|
,(progn
|
|
(string-match "[a-z]+" (cdr elem))
|
|
(match-string 0 (cdr elem)))))
|
|
my/latex-section-snippets))
|
|
|
|
(dolist (elem my/latex-section-snippets)
|
|
(let* ((key (nth 0 elem))
|
|
(value (nth 1 elem))
|
|
(desc (nth 2 elem))
|
|
(star-index (string-match "\{\$1\}" value)))
|
|
(add-to-list 'my/latex-section-snippets
|
|
`(,(concat key "*")
|
|
,(concat
|
|
(substring value 0 star-index)
|
|
"*"
|
|
(substring value star-index))
|
|
,(concat desc " with *")))
|
|
(add-to-list 'my/latex-section-snippets
|
|
`(,(concat key "l")
|
|
,(concat value "%\n\\label{sec:$2}")
|
|
,(concat desc " with label")))))
|
|
|
|
(dolist (elem my/latex-section-snippets)
|
|
(setf (nth 1 elem) (concat (nth 1 elem) "\n$0")))
|
|
|
|
(yas-define-snippets
|
|
'latex-mode
|
|
my/latex-section-snippets)
|
|
#+end_src
|
|
** Other markup languages
|
|
*** Markdown
|
|
#+begin_src emacs-lisp
|
|
(use-package markdown-mode
|
|
:straight t
|
|
:mode "\\.md\\'"
|
|
:config
|
|
(setq markdown-command
|
|
(concat
|
|
"pandoc"
|
|
" --from=markdown --to=html"
|
|
" --standalone --mathjax --highlight-style=pygments"
|
|
" --css=pandoc.css"
|
|
" --quiet"
|
|
))
|
|
(setq markdown-live-preview-delete-export 'delete-on-export)
|
|
(setq markdown-asymmetric-header t)
|
|
(setq markdown-open-command "/home/pavel/bin/scripts/chromium-sep")
|
|
(add-hook 'markdown-mode-hook #'smartparens-mode)
|
|
(general-define-key
|
|
:keymaps 'markdown-mode-map
|
|
"M-<left>" 'markdown-promote
|
|
"M-<right>" 'markdown-demote))
|
|
|
|
;; (use-package livedown
|
|
;; :straight (:host github :repo "shime/emacs-livedown")
|
|
;; :commands livedown-preview
|
|
;; :config
|
|
;; (setq livedown-browser "qutebrowser"))
|
|
|
|
#+end_src
|
|
*** PlantUML
|
|
#+begin_src emacs-lisp
|
|
(use-package plantuml-mode
|
|
:straight t
|
|
:mode "(\\.\\(plantuml?\\|uml\\|puml\\)\\'"
|
|
:config
|
|
(setq plantuml-executable-path "/usr/bin/plantuml")
|
|
(setq plantuml-default-exec-mode 'executable)
|
|
(add-to-list 'auto-mode-alist '("\\.plantuml\\'" . plantuml-mode))
|
|
(add-to-list 'auto-mode-alist '("\\.uml\\'" . plantuml-mode))
|
|
(add-hook 'plantuml-mode-hook #'smartparens-mode))
|
|
|
|
(general-nmap
|
|
:keymaps 'plantuml-mode-map
|
|
"RET" 'plantuml-preview)
|
|
|
|
#+end_src
|
|
*** LanguageTool
|
|
LanguageTool is a great offline spell checker. For some reason the download link is nowhere to be found on the home page, so it is listed in the references as well.
|
|
|
|
References:
|
|
- [[https://languagetool.org/][LanguageTool homepage]]
|
|
- [[https://dev.languagetool.org/http-server][LanguageTool http server]]
|
|
- [[https://github.com/mhayashi1120/Emacs-langtool][LanguageTool for Emacs repo]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package langtool
|
|
:straight t
|
|
:commands (langtool-check)
|
|
:config
|
|
(setq langtool-language-tool-server-jar "/home/pavel/Programs/LanguageTool-5.1/languagetool-server.jar")
|
|
(setq langtool-mother-tongue "ru")
|
|
(setq langtool-default-language "en-US"))
|
|
|
|
(my-leader-def
|
|
:infix "L"
|
|
"c" 'langtool-check
|
|
"s" 'langtool-server-stop
|
|
"d" 'langtool-check-done
|
|
"n" 'langtool-goto-next-error
|
|
"p" 'langtool-goto-previous-error
|
|
"l" 'langtool-correct-buffer)
|
|
#+end_src
|
|
** Lisp
|
|
These are your father's parentheses. Elegant weapons for a more... civilized age.
|
|
*** Meta Lisp
|
|
Some packages for editing various Lisps.
|
|
#+begin_src emacs-lisp
|
|
(use-package lispy
|
|
:commands (lispy-mode)
|
|
:straight t)
|
|
|
|
(use-package lispyville
|
|
:hook (lispy-mode . lispyville-mode)
|
|
:straight t)
|
|
|
|
(sp-with-modes sp-lisp-modes
|
|
(sp-local-pair "'" nil :actions nil))
|
|
#+end_src
|
|
*** Emacs Lisp
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'emacs-lisp-mode-hook #'aggressive-indent-mode)
|
|
;; (add-hook 'emacs-lisp-mode-hook #'smartparens-strict-mode)
|
|
(add-hook 'emacs-lisp-mode-hook #'lispy-mode)
|
|
#+end_src
|
|
*** Common lisp
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'lisp-mode-hook #'aggressive-indent-mode)
|
|
;; (add-hook 'emacs-lisp-mode-hook #'smartparens-strict-mode)
|
|
(add-hook 'lisp-mode-hook #'lispy-mode)
|
|
#+end_src
|
|
*** Clojure
|
|
#+begin_src emacs-lisp
|
|
(use-package clojure-mode
|
|
:straight t
|
|
:mode "\\.clj[sc]?\\'"
|
|
:config
|
|
;; (add-hook 'clojure-mode-hook #'smartparens-strict-mode)
|
|
(add-hook 'clojure-mode-hook #'lispy-mode)
|
|
(add-hook 'clojure-mode-hook #'aggressive-indent-mode))
|
|
|
|
(use-package cider
|
|
:mode "\\.clj[sc]?\\'"
|
|
:straight t)
|
|
#+end_src
|
|
*** Hy
|
|
Python requirements:
|
|
- =hy=
|
|
- =jedhy=
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package hy-mode
|
|
:straight t
|
|
:mode "\\.hy\\'"
|
|
:config
|
|
(add-hook 'hy-mode-hook #'lispy-mode)
|
|
(add-hook 'hy-mode-hook #'aggressive-indent-mode))
|
|
#+end_src
|
|
*** Scheme
|
|
#+begin_src emacs-lisp
|
|
(use-package geiser
|
|
:straight t
|
|
:if (not my/lowpower)
|
|
:config
|
|
(setq geiser-default-implementation 'guile))
|
|
|
|
(use-package geiser-guile
|
|
:straight t
|
|
:after geiser)
|
|
|
|
(add-hook 'scheme-mode-hook #'aggressive-indent-mode)
|
|
(add-hook 'scheme-mode-hook #'lispy-mode)
|
|
#+end_src
|
|
*** CLIPS
|
|
An honorary Lisp
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package clips-mode
|
|
:straight t
|
|
:mode "\\.cl\\'"
|
|
:config
|
|
(add-hook 'clips-mode 'lispy-mode))
|
|
#+end_src
|
|
** Python
|
|
Use [[https://github.com/Microsoft/python-language-server][Microsoft Language Server for Python]].
|
|
|
|
For some reason it doesn't use pipenv python executable, so here is a small workaround.
|
|
#+begin_src emacs-lisp
|
|
(setq my/pipenv-python-alist '())
|
|
|
|
(defun my/get-pipenv-python ()
|
|
(let ((default-directory (projectile-project-root)))
|
|
(if (file-exists-p "Pipfile")
|
|
(let ((asc (assoc default-directory my/pipenv-python-alist)))
|
|
(if asc
|
|
(cdr asc)
|
|
(let ((python-executable
|
|
(string-trim (shell-command-to-string "PIPENV_IGNORE_VIRTUALENVS=1 pipenv run which python"))))
|
|
(if (string-match-p ".*not found.*" python-executable)
|
|
(message "Pipfile found, but not pipenv executable!")
|
|
(message (format "Found pipenv python: %s" python-executable))
|
|
(add-to-list 'my/pipenv-python-alist (cons default-directory python-executable))
|
|
python-executable))))
|
|
"python")))
|
|
|
|
(use-package lsp-pyright
|
|
:straight t
|
|
:defer t
|
|
:if (not my/slow-ssh)
|
|
:hook (python-mode . (lambda ()
|
|
(require 'lsp-pyright)
|
|
(setq-local lsp-pyright-python-executable-cmd (my/get-pipenv-python))
|
|
(lsp))))
|
|
|
|
(add-hook 'python-mode-hook #'smartparens-mode)
|
|
(add-hook 'python-mode-hook #'hs-minor-mode)
|
|
#+end_src
|
|
*** pipenv
|
|
[[https://github.com/pypa/pipenv][Pipenv]] is a package manager for Python.
|
|
|
|
Automatically creates & manages virtualenvs and stores data in =Pipfile= and =Pipfile.lock= (like npm's =package.json= and =package-lock.json=).
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package pipenv
|
|
:straight t
|
|
:hook (python-mode . pipenv-mode)
|
|
:if (not my/slow-ssh)
|
|
:init
|
|
(setq
|
|
pipenv-projectile-after-switch-function
|
|
#'pipenv-projectile-after-switch-extended))
|
|
#+end_src
|
|
*** yapf
|
|
[[https://github.com/google/yapf][yapf]] is a formatter for Python files.
|
|
|
|
| Guix dependency |
|
|
|-----------------|
|
|
| python-yapf |
|
|
|
|
References:
|
|
- [[https://github.com/google/yapf][yapf repo]]
|
|
- [[https://github.com/JorisE/yapfify][yapfify.el repo]]
|
|
#+begin_src emacs-lisp
|
|
(use-package yapfify
|
|
:straight (:repo "JorisE/yapfify" :host github)
|
|
:commands (yapfify-region
|
|
yapfify-buffer
|
|
yapfify-region-or-buffer
|
|
yapf-mode))
|
|
#+end_src
|
|
|
|
Global config:
|
|
#+begin_src conf-windows :tangle .config/yapf/style :comments link
|
|
[style]
|
|
based_on_style = facebook
|
|
column_limit = 80
|
|
#+end_src
|
|
*** isort
|
|
[[https://github.com/PyCQA/isort][isort]] is a Python package to sort Python imports.
|
|
|
|
| Guix dependency |
|
|
|-----------------|
|
|
| python-isort |
|
|
|
|
References:
|
|
- [[https://pycqa.github.io/isort/][isort docs]]
|
|
- [[https://github.com/paetzke/py-isort.el][py-isort.el repo]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package py-isort
|
|
:straight t
|
|
:commands (py-isort-buffer py-isort-region))
|
|
#+end_src
|
|
|
|
The following bindings calls yapf & isort on the buffer
|
|
#+begin_src emacs-lisp
|
|
(my-leader-def
|
|
:keymaps 'python-mode-map
|
|
"rr" (lambda ()
|
|
(interactive)
|
|
(unless (and (fboundp #'org-src-edit-buffer-p) (org-src-edit-buffer-p))
|
|
(py-isort-buffer))
|
|
(yapfify-buffer)))
|
|
#+end_src
|
|
*** sphinx-doc
|
|
A package to generate sphinx-compatible docstrings.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package sphinx-doc
|
|
:straight t
|
|
:hook (python-mode . sphinx-doc-mode)
|
|
:config
|
|
(my-leader-def
|
|
:keymaps 'sphinx-doc-mode-map
|
|
"rd" 'sphinx-doc))
|
|
#+end_src
|
|
*** pytest
|
|
[[https://docs.pytest.org/en/6.2.x/][pytest]] is an unit testing framework for Python.
|
|
|
|
Once again a function to set pytest executable from pipenv.
|
|
|
|
References:
|
|
- [[https://docs.pytest.org/en/6.2.x/][pytest docs]]
|
|
- [[https://github.com/wbolster/emacs-python-pytest][emacs-python-pytest]]
|
|
|
|
#+begin_src emacs-lisp :noweb yes
|
|
(defun my/set-pipenv-pytest ()
|
|
(setq-local
|
|
python-pytest-executable
|
|
(concat (my/get-pipenv-python) " -m pytest")))
|
|
|
|
(use-package python-pytest
|
|
:straight t
|
|
:commands (python-pytest-dispatch)
|
|
:init
|
|
(my-leader-def
|
|
:keymaps 'python-mode-map
|
|
:infix "t"
|
|
"t" 'python-pytest-dispatch)
|
|
:config
|
|
<<override-pytest-run>>
|
|
(add-hook 'python-mode-hook #'my/set-pipenv-pytest)
|
|
(when (derived-mode-p 'python-mode)
|
|
(my/set-pipenv-pytest)))
|
|
#+end_src
|
|
**** Fix comint buffer width
|
|
For some reason default comint output width is way too large.
|
|
|
|
To fix that, I've modified the following function in the =python-pytest= package.
|
|
#+begin_src emacs-lisp :noweb-ref override-pytest-run :tangle no
|
|
(cl-defun python-pytest--run-as-comint (&key command)
|
|
"Run a pytest comint session for COMMAND."
|
|
(let* ((buffer (python-pytest--get-buffer))
|
|
(process (get-buffer-process buffer)))
|
|
(with-current-buffer buffer
|
|
(when (comint-check-proc buffer)
|
|
(unless (or compilation-always-kill
|
|
(yes-or-no-p "Kill running pytest process?"))
|
|
(user-error "Aborting; pytest still running")))
|
|
(when process
|
|
(delete-process process))
|
|
(let ((inhibit-read-only t))
|
|
(erase-buffer))
|
|
(unless (eq major-mode 'python-pytest-mode)
|
|
(python-pytest-mode))
|
|
(compilation-forget-errors)
|
|
(display-buffer buffer)
|
|
(setq command (format "export COLUMNS=%s; %s"
|
|
(- (window-width (get-buffer-window buffer)) 5)
|
|
command))
|
|
(insert (format "cwd: %s\ncmd: %s\n\n" default-directory command))
|
|
(setq python-pytest--current-command command)
|
|
(when python-pytest-pdb-track
|
|
(add-hook
|
|
'comint-output-filter-functions
|
|
'python-pdbtrack-comint-output-filter-function
|
|
nil t))
|
|
(run-hooks 'python-pytest-setup-hook)
|
|
(make-comint-in-buffer "pytest" buffer "bash" nil "-c" command)
|
|
(run-hooks 'python-pytest-started-hook)
|
|
(setq process (get-buffer-process buffer))
|
|
(set-process-sentinel process #'python-pytest--process-sentinel))))
|
|
#+end_src
|
|
*** code-cells
|
|
Support for text with magic comments.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package code-cells
|
|
:straight t
|
|
:commands (code-cells-mode))
|
|
#+end_src
|
|
*** tensorboard
|
|
A function to start up [[https://www.tensorflow.org/tensorboard][TensorBoard]].
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq my/tensorboard-buffer "TensorBoard-out")
|
|
|
|
(defun my/tensorboard ()
|
|
(interactive)
|
|
(start-process
|
|
"tensorboard"
|
|
my/tensorboard-buffer
|
|
"tensorboard"
|
|
"serve"
|
|
"--logdir"
|
|
(car (find-file-read-args "Directory: " t)))
|
|
(display-buffer my/tensorboard-buffer))
|
|
#+end_src
|
|
** Java
|
|
#+begin_src emacs-lisp
|
|
(use-package lsp-java
|
|
:straight t
|
|
:after (lsp)
|
|
:config
|
|
(setq lsp-java-jdt-download-url "https://download.eclipse.org/jdtls/milestones/0.57.0/jdt-language-server-0.57.0-202006172108.tar.gz"))
|
|
|
|
(add-hook 'java-mode-hook #'smartparens-mode)
|
|
;; (add-hook 'java-mode-hook #'hs-minor-mode)
|
|
(my/set-smartparens-indent 'java-mode)
|
|
#+end_src
|
|
** Go
|
|
#+begin_src emacs-lisp
|
|
(use-package go-mode
|
|
:straight t
|
|
:mode "\\.go\\'"
|
|
:config
|
|
(my/set-smartparens-indent 'go-mode)
|
|
(add-hook 'go-mode-hook #'smartparens-mode)
|
|
(add-hook 'go-mode-hook #'hs-minor-mode))
|
|
#+end_src
|
|
** .NET
|
|
*** C#
|
|
#+begin_src emacs-lisp
|
|
(use-package csharp-mode
|
|
:straight t
|
|
:mode "\\.cs\\'"
|
|
:config
|
|
(add-hook 'csharp-mode-hook #'csharp-tree-sitter-mode)
|
|
(add-hook 'csharp-tree-sitter-mode-hook #'smartparens-mode)
|
|
(add-hook 'csharp-mode-hook #'hs-minor-mode)
|
|
(my/set-smartparens-indent 'csharp-tree-sitter-mode))
|
|
#+end_src
|
|
*** MSBuild
|
|
#+begin_src emacs-lisp
|
|
(use-package csproj-mode
|
|
:straight t
|
|
:mode "\\.csproj\\'"
|
|
:config
|
|
(add-hook 'csproj-mode #'smartparens-mode))
|
|
#+end_src
|
|
** fish
|
|
#+begin_src emacs-lisp
|
|
(use-package fish-mode
|
|
:straight t
|
|
:mode "\\.fish\\'"
|
|
:config
|
|
(add-hook 'fish-mode-hook #'smartparens-mode))
|
|
#+end_src
|
|
** sh
|
|
#+begin_src emacs-lisp
|
|
(add-hook 'sh-mode-hook #'smartparens-mode)
|
|
#+end_src
|
|
** Haskell
|
|
#+begin_src emacs-lisp
|
|
(use-package haskell-mode
|
|
:straight t
|
|
:mode "\\.hs\\'")
|
|
|
|
(use-package lsp-haskell
|
|
:straight t
|
|
:after (lsp haskell-mode))
|
|
#+end_src
|
|
** JSON
|
|
#+begin_src emacs-lisp
|
|
(use-package json-mode
|
|
:straight t
|
|
:mode "\\.json\\'"
|
|
:config
|
|
(add-hook 'json-mode #'smartparens-mode)
|
|
(add-hook 'json-mode #'hs-minor-mode)
|
|
(my/set-smartparens-indent 'json-mode))
|
|
#+end_src
|
|
** YAML
|
|
#+begin_src emacs-lisp
|
|
(use-package yaml-mode
|
|
:straight t
|
|
:mode "\\.yml\\'"
|
|
:config
|
|
(add-hook 'yaml-mode-hook 'smartparens-mode)
|
|
(add-hook 'yaml-mode-hook 'highlight-indent-guides-mode)
|
|
(add-to-list 'auto-mode-alist '("\\.yml\\'" . yaml-mode)))
|
|
#+end_src
|
|
** .env
|
|
#+begin_src emacs-lisp
|
|
(use-package dotenv-mode
|
|
:straight t
|
|
:mode "\\.env\\..*\\'")
|
|
#+end_src
|
|
** CSV
|
|
#+begin_src emacs-lisp
|
|
(use-package csv-mode
|
|
:straight t
|
|
:mode "\\.csv\\'")
|
|
#+end_src
|
|
** OFF (OFF) PDF
|
|
A decent package to view PDFs in Emacs, but I prefer Zathura.
|
|
|
|
References:
|
|
- https://github.com/vedang/pdf-tools/
|
|
|
|
#+begin_src emacs-lisp :tangle no
|
|
(use-package pdf-tools
|
|
:straight t
|
|
:commands (pdf-tools-install))
|
|
#+end_src
|
|
** Docker
|
|
#+begin_src emacs-lisp
|
|
(use-package dockerfile-mode
|
|
:mode "Dockerfile\\'"
|
|
:straight t
|
|
:config
|
|
(add-hook 'dockerfile-mode 'smartparens-mode))
|
|
#+end_src
|
|
* Apps & Misc
|
|
** Managing dotfiles
|
|
A bunch of functions for managing dotfiles with yadm.
|
|
|
|
*** Open Emacs config
|
|
#+begin_src emacs-lisp
|
|
(defun my/edit-configuration ()
|
|
"Open the init file."
|
|
(interactive)
|
|
(find-file "~/Emacs.org"))
|
|
|
|
;; (defun my/edit-exwm-configuration ()
|
|
;; "Open the exwm config file."
|
|
;; (interactive)
|
|
;; (find-file "~/.emacs.d/exwm.org"))
|
|
|
|
(general-define-key "C-c c" 'my/edit-configuration)
|
|
;; (general-define-key "C-c C" 'my/edit-exwm-configuration)
|
|
(my-leader-def "cc" 'my/edit-configuration)
|
|
#+end_src
|
|
*** Open Magit for yadm
|
|
Idea:
|
|
|
|
- [[https://www.reddit.com/r/emacs/comments/gjukb3/yadm_magit/]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(with-eval-after-load 'tramp
|
|
(add-to-list 'tramp-methods
|
|
`("yadm"
|
|
(tramp-login-program "yadm")
|
|
(tramp-login-args (("enter")))
|
|
(tramp-login-env (("SHELL") "/bin/sh"))
|
|
(tramp-remote-shell "/bin/sh")
|
|
(tramp-remote-shell-args ("-c")))))
|
|
|
|
|
|
(defun my/yadm-magit ()
|
|
(interactive)
|
|
(magit-status "/yadm::"))
|
|
|
|
(my-leader-def "cm" 'my/yadm-magit)
|
|
#+end_src
|
|
*** Open a dotfile
|
|
Open a file managed by yadm.
|
|
|
|
#+begin_src emacs-lisp
|
|
(defun my/open-yadm-file ()
|
|
"Open a file managed by yadm"
|
|
(interactive)
|
|
(find-file
|
|
(concat
|
|
(file-name-as-directory (getenv "HOME"))
|
|
(completing-read
|
|
"yadm files: "
|
|
(split-string
|
|
(shell-command-to-string "yadm ls-files $HOME --full-name") "\n")))))
|
|
|
|
(general-define-key "C-c f" 'my/open-yadm-file)
|
|
(my-leader-def "cf" 'my/open-yadm-file)
|
|
#+end_src
|
|
|
|
** Notmuch
|
|
My notmuch config now resides in [[file:Mail.org][Mail.org]].
|
|
|
|
#+begin_src emacs-lisp
|
|
(load-file (expand-file-name "mail.el" user-emacs-directory))
|
|
#+end_src
|
|
** Elfeed
|
|
[[https://github.com/skeeto/elfeed][elfeed]] is an Emacs RSS client.
|
|
|
|
The advice there sets =shr-use-fonts= to nil while rendering HTML, so the =elfeed-show= buffer will use monospace font.
|
|
|
|
Using my own fork until the modifications are merged into master.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package elfeed
|
|
:straight (:repo "SqrtMinusOne/elfeed" :host github)
|
|
:commands (elfeed)
|
|
:init
|
|
(my-leader-def "ae" 'elfeed)
|
|
:config
|
|
(setq elfeed-db-directory "~/.elfeed")
|
|
(setq elfeed-enclosure-default-dir (expand-file-name "~"))
|
|
(advice-add #'elfeed-insert-html
|
|
:around
|
|
(lambda (fun &rest r)
|
|
(let ((shr-use-fonts nil))
|
|
(apply fun r))))
|
|
(evil-collection-define-key 'normal 'elfeed-search-mode-map
|
|
"o" #'my/elfeed-search-filter-source
|
|
"c" #'elfeed-search-clear-filter
|
|
"gl" (lambda () (interactive) (elfeed-search-set-filter "+later")))
|
|
(evil-collection-define-key 'normal 'elfeed-show-mode-map
|
|
"ge" #'my/elfeed-show-visit-eww))
|
|
#+end_src
|
|
|
|
[[https://github.com/remyhonig/elfeed-org][elfeed-org]] allows configuring Elfeed feeds with an Org file.
|
|
#+begin_src emacs-lisp
|
|
(use-package elfeed-org
|
|
:straight t
|
|
:after (elfeed)
|
|
:config
|
|
(setq rmh-elfeed-org-files '("~/.emacs.d/elfeed.org"))
|
|
(elfeed-org))
|
|
#+end_src
|
|
|
|
*** Some additions
|
|
Filter elfeed search buffer by the feed under the cursor.
|
|
#+begin_src emacs-lisp
|
|
(defun my/elfeed-search-filter-source (entry)
|
|
"Filter elfeed search buffer by the feed under cursor."
|
|
(interactive (list (elfeed-search-selected :ignore-region)))
|
|
(when (elfeed-entry-p entry)
|
|
(elfeed-search-set-filter
|
|
(concat
|
|
"@6-months-ago "
|
|
"+unread "
|
|
"="
|
|
(replace-regexp-in-string
|
|
(rx "?" (* not-newline) eos)
|
|
""
|
|
(elfeed-feed-url (elfeed-entry-feed entry)))))))
|
|
#+end_src
|
|
|
|
Open a URL with eww.
|
|
#+begin_src emacs-lisp
|
|
(defun my/elfeed-show-visit-eww ()
|
|
"Visit the current entry in eww"
|
|
(interactive)
|
|
(let ((link (elfeed-entry-link elfeed-show-entry)))
|
|
(when link
|
|
(eww link))))
|
|
#+end_src
|
|
*** YouTube
|
|
| Guix dependency |
|
|
|-----------------|
|
|
| mpv |
|
|
| youtube-dl |
|
|
|
|
A function to open YouTube link with mpv
|
|
|
|
#+begin_src emacs-lisp
|
|
(setq my/youtube-dl-quality-list
|
|
'("bestvideo[height<=720]+bestaudio/best[height<=720]"
|
|
"bestvideo[height<=480]+bestaudio/best[height<=480]"
|
|
"bestvideo[height<=1080]+bestaudio/best[height<=1080]"))
|
|
|
|
(setq my/youtube-dl-quality-default "bestvideo[height<=720]+bestaudio/best[height<=720]")
|
|
|
|
(defun my/open-youtube-video (link)
|
|
"Open Youtube URL with mpv"
|
|
(interactive "MURL: ")
|
|
(let ((quality (completing-read "Quality: " my/youtube-dl-quality-list nil t))
|
|
(watch-id (cadr
|
|
(assoc "watch?v"
|
|
(url-parse-query-string
|
|
(substring
|
|
(url-filename
|
|
(url-generic-parse-url link))
|
|
1))))))
|
|
(if (not watch-id)
|
|
(message "Can't find youtube link")
|
|
(let ((yt-link (concat "https://www.youtube.com/watch?v=" watch-id))
|
|
(watch-name (concat "mpv-" watch-id)))
|
|
(start-process watch-name watch-name "mpv" yt-link
|
|
(format "--ytdl-format=%s" quality))))))
|
|
#+end_src
|
|
|
|
And a function to open YouTube link from elfeed
|
|
#+begin_src emacs-lisp
|
|
(defun my/elfeed-open-mpv ()
|
|
(interactive)
|
|
"Open MPV for the current entry"
|
|
(my/open-youtube-video (elfeed-entry-link elfeed-show-entry)))
|
|
|
|
(with-eval-after-load 'elfeed
|
|
(evil-collection-define-key 'normal 'elfeed-show-mode-map
|
|
"gm" #'my/elfeed-open-mpv))
|
|
#+end_src
|
|
** man & tldr
|
|
[[https://tldr.sh/][tldr]] is a collaborative project providing cheatsheets for various console commands
|
|
|
|
For some reason, the built-in download is broken, so I use my own function
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package tldr
|
|
:straight t
|
|
:commands (tldr)
|
|
:config
|
|
(setq tldr-source-zip-url "https://github.com/tldr-pages/tldr/archive/refs/heads/main.zip")
|
|
|
|
(defun tldr-update-docs ()
|
|
(interactive)
|
|
(shell-command-to-string (format "curl -L %s --output %s" tldr-source-zip-url tldr-saved-zip-path))
|
|
(when (file-exists-p "/tmp/tldr")
|
|
(delete-directory "/tmp/tldr" t))
|
|
(shell-command-to-string (format "unzip -d /tmp/tldr/ %s" tldr-saved-zip-path) nil nil)
|
|
(when (file-exists-p tldr-directory-path)
|
|
(delete-directory tldr-directory-path 'recursive 'no-trash))
|
|
(shell-command-to-string (format "mv %s %s" "/tmp/tldr/tldr-main" tldr-directory-path))))
|
|
|
|
(my-leader-def "hT" 'tldr)
|
|
|
|
(setq Man-width-max 180)
|
|
(my-leader-def "hM" 'man)
|
|
#+end_src
|
|
** ERC
|
|
ERC is a built-it Emacs IRC client.
|
|
|
|
#+begin_src emacs-lisp
|
|
(my-leader-def "ai" #'erc-tls)
|
|
#+end_src
|
|
|
|
A plugin to highlight IRC nicknames:
|
|
#+begin_src emacs-lisp
|
|
(use-package erc-hl-nicks
|
|
:hook (erc-mode . erc-hl-nicks-mode)
|
|
:straight t)
|
|
#+end_src
|
|
|
|
Config of my ZNC instance.
|
|
#+begin_src emacs-lisp
|
|
(setq erc-server "sqrtminusone.xyz")
|
|
(setq erc-port 1984)
|
|
(setq erc-nick "sqrtminusone")
|
|
(setq erc-user-full-name "Pavel Korytov")
|
|
(setq erc-track-shorten-start 8)
|
|
#+end_src
|
|
|
|
Kill buffer on part.
|
|
#+begin_src emacs-lisp
|
|
(setq erc-kill-buffer-on-part t)
|
|
#+end_src
|
|
|
|
ZNC support. Seems to provide a few nice features for ZNC.
|
|
#+begin_src emacs-lisp
|
|
(use-package znc
|
|
:straight t
|
|
:after (erc))
|
|
#+end_src
|
|
** Docker
|
|
A package to manage docker containers from Emacs.
|
|
|
|
The file =progidy-config.el= sets variable =my/docker-directories=, which allows to
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package docker
|
|
:straight t
|
|
:commands (docker)
|
|
:init
|
|
(my-leader-def "ao" 'docker))
|
|
#+end_src
|
|
|
|
By default, docker commands are ran in =default-directory=. Even worse, transient doesn't allow to set =default-directory= temporarily, via =let=. But often I don't want to change =default-directory= of a buffer (e.g. via Dired) to run a command from there.
|
|
|
|
So I decided to implement a following advice:
|
|
#+begin_src emacs-lisp
|
|
(setq my/selected-docker-directory nil)
|
|
|
|
(defun my/docker-override-dir (fun &rest args)
|
|
(let ((default-directory (or my/selected-docker-directory default-directory)))
|
|
(setq my/selected-docker-directory nil)
|
|
(apply fun args)))
|
|
#+end_src
|
|
|
|
It overrides =default-directory= for the first launch of a function. Now, add the advice to the required functions from =docker.el=:
|
|
#+begin_src emacs-lisp
|
|
(with-eval-after-load 'docker
|
|
(advice-add #'docker-compose-run-docker-compose-async :around #'my/docker-override-dir)
|
|
(advice-add #'docker-compose-run-docker-compose :around #'my/docker-override-dir)
|
|
(advice-add #'docker-run-docker-async :around #'my/docker-override-dir)
|
|
(advice-add #'docker-run-docker :around #'my/docker-override-dir))
|
|
#+end_src
|
|
|
|
And here is a function which prompts user for the directory. File =progidy-config.el= sets an alist of possible directories, look the section about [[*Progidy][progidy]].
|
|
#+begin_src emacs-lisp
|
|
(defun my/docker-from-dir ()
|
|
(interactive)
|
|
(when (not (boundp 'my/docker-directories))
|
|
(load (concat user-emacs-directory "prodigy-config")))
|
|
(let* ((directories
|
|
(mapcar
|
|
(lambda (el) (cons (format "%-30s %s" (car el) (cdr el)) (cdr el)))
|
|
my/docker-directories))
|
|
(selected-directory
|
|
(cdr (assoc (completing-read "Docker: " directories nil nil "^")
|
|
directories))))
|
|
(setq my/selected-docker-directory selected-directory)
|
|
(docker)))
|
|
|
|
(my-leader-def "aO" 'my/docker-from-dir)
|
|
#+end_src
|
|
** Progidy
|
|
[[https://github.com/rejeep/prodigy.el][prodigy.el]] is a package to run various services. I've previously used tmuxp + tmux, but want to try this as well.
|
|
|
|
The actual service definitions are in the =~/.emacs.d/prodigy.org=, which tangles to =prodigy-config.el=. Both files are encrypted in yadm, as they contain personal data.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package prodigy
|
|
:straight t
|
|
:commands (prodigy)
|
|
:init
|
|
(my-leader-def "ap" 'prodigy)
|
|
:config
|
|
(when (not (boundp 'my/docker-directories))
|
|
(load (concat user-emacs-directory "prodigy-config")))
|
|
(evil-collection-define-key 'normal 'prodigy-view-mode-map
|
|
(kbd "C-h") 'evil-window-left
|
|
(kbd "C-l") 'evil-window-right
|
|
(kbd "C-k") 'evil-window-up
|
|
(kbd "C-j") 'evil-window-down))
|
|
#+end_src
|
|
** Google Translate
|
|
Emacs interface to Google Translate.
|
|
|
|
Can't make it load lazily for some strange reason.
|
|
|
|
References:
|
|
- [[https://github.com/atykhonov/google-translate][google-translate repo]]
|
|
- [[https://github.com/atykhonov/google-translate/issues/137#issuecomment-728278849][issue with ttk error fix]]
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package google-translate
|
|
:straight t
|
|
:functions (my-google-translate-at-point google-translate--search-tkk)
|
|
:custom
|
|
(google-translate-backend-method 'curl)
|
|
:config
|
|
(require 'facemenu)
|
|
(defun google-translate--search-tkk ()
|
|
"Search TKK."
|
|
(list 430675 2721866130))
|
|
(defun my-google-translate-at-point()
|
|
"reverse translate if prefix"
|
|
(interactive)
|
|
(if current-prefix-arg
|
|
(google-translate-at-point)
|
|
(google-translate-at-point-reverse)))
|
|
(setq google-translate-translation-directions-alist
|
|
'(("en" . "ru")
|
|
("ru" . "en"))))
|
|
|
|
(my-leader-def
|
|
"atp" 'google-translate-at-point
|
|
"atP" 'google-translate-at-point-reverse
|
|
"atq" 'google-translate-query-translate
|
|
"atQ" 'google-translate-query-translate-reverse
|
|
"att" 'google-translate-smooth-translate)
|
|
#+end_src
|
|
** Pomidor
|
|
A simple pomodoro technique timer.
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package pomidor
|
|
:straight t
|
|
:commands (pomidor)
|
|
:init
|
|
(my-leader-def "aP" #'pomidor)
|
|
:config
|
|
(setq pomidor-sound-tick nil)
|
|
(setq pomidor-sound-tack nil)
|
|
(evil-collection-define-key 'normal 'pomidor-mode-map
|
|
(kbd "q") #'quit-window
|
|
(kbd "Q") #'pomidor-quit
|
|
(kbd "R") #'pomidor-reset
|
|
(kbd "h") #'pomidor-hold
|
|
(kbd "H") #'pomidor-unhold
|
|
(kbd "RET") #'pomidor-stop
|
|
(kbd "M-RET") #'pomidor-break))
|
|
#+end_src
|
|
** EWW
|
|
Emacs built-in web browser. +I wonder if anyone actually uses it.+
|
|
|
|
I use it occasionally to open links in elfeed.
|
|
|
|
#+begin_src emacs-lisp
|
|
(defun my/toggle-shr-use-fonts ()
|
|
"Toggle the shr-use-fonts variable in buffer"
|
|
(interactive)
|
|
(setq-local shr-use-fonts (not shr-use-fonts)))
|
|
|
|
(my-leader-def "aw" 'eww)
|
|
|
|
(general-define-key
|
|
:keymaps 'eww-mode-map
|
|
"+" 'text-scale-increase
|
|
"-" 'text-scale-decrease)
|
|
#+end_src
|
|
** proced
|
|
proced is a Emacs built-it process viewer, like top.
|
|
|
|
#+begin_src emacs-lisp
|
|
(my-leader-def "ah" 'proced)
|
|
(add-hook 'proced-mode-hook (lambda ()
|
|
(visual-line-mode -1)
|
|
(setq-local truncate-lines t)))
|
|
#+end_src
|
|
** screenshot.el
|
|
Tecosaur's plugin to make beautiful code screenshots.
|
|
|
|
| Guix dependency |
|
|
|-----------------|
|
|
| imagemagick |
|
|
|
|
#+begin_src emacs-lisp
|
|
(use-package screenshot
|
|
:straight (:repo "tecosaur/screenshot" :host github :files ("screenshot.el"))
|
|
:commands (screenshot)
|
|
:init
|
|
(my-leader-def "S" 'screenshot))
|
|
#+end_src
|
|
** Snow
|
|
#+begin_src emacs-lisp
|
|
(use-package snow
|
|
:straight (:repo "alphapapa/snow.el" :host github)
|
|
:commands (snow))
|
|
#+end_src
|
|
** Zone
|
|
#+begin_src emacs-lisp
|
|
(use-package zone
|
|
:ensure nil
|
|
:config
|
|
(setq original-zone-programs (copy-sequence zone-programs)))
|
|
|
|
(defun my/zone-with-select ()
|
|
(interactive)
|
|
(ivy-read "Zone programs"
|
|
(cl-pairlis
|
|
(cl-mapcar 'symbol-name original-zone-programs)
|
|
original-zone-programs)
|
|
:action (lambda (elem)
|
|
(setq zone-programs (vector (cdr elem)))
|
|
(zone))))
|
|
#+end_src
|
|
** Discord integration
|
|
Integration with Discord.
|
|
|
|
Shows which file is being edited in Emacs.
|
|
#+begin_src emacs-lisp
|
|
(use-package elcord
|
|
:straight t
|
|
:if (and (or
|
|
(string= (system-name) "indigo")
|
|
(string= (system-name) "eminence"))
|
|
(not my/slow-ssh))
|
|
:config
|
|
(elcord-mode)
|
|
(add-to-list 'elcord-boring-buffers-regexp-list
|
|
(rx bos (+ num) "-" (+ num) "-" (+ num) ".org" eos))
|
|
(add-to-list 'elcord-boring-buffers-regexp-list
|
|
(rx bos (= 14 num) "-" (* not-newline) ".org" eos)))
|
|
#+end_src
|
|
** Guix
|
|
#+begin_src emacs-lisp
|
|
(use-package guix
|
|
:straight t
|
|
:commands (guix)
|
|
:init
|
|
(my-leader-def "ag" 'guix))
|
|
#+end_src
|
|
* Guix settings
|
|
| Guix dependency | Description |
|
|
|---------------------+-------------------------------|
|
|
| emacs-vterm | A vterm package |
|
|
| ripgrep | A recursive search tool |
|
|
| the-silver-searcher | Another recursive search tool |
|
|
|
|
#+NAME: packages
|
|
#+begin_src emacs-lisp :tangle no
|
|
(my/format-guix-dependencies)
|
|
#+end_src
|
|
|
|
#+begin_src scheme :tangle .config/guix/manifests/emacs.scm :noweb yes
|
|
(specifications->manifest
|
|
'("emacs-native-comp"
|
|
<<packages()>>))
|
|
#+end_src
|