#+TITLE: Emacs config #+AUTHOR: Yorick van Pelt * Prelims ** Workarounds? #+BEGIN_SRC emacs-lisp (setq max-lisp-eval-depth 10000) (setq max-specpdl-size 13000) #+END_SRC ** start server #+BEGIN_SRC emacs-lisp (server-start) #+END_SRC ** use-package compile-time #+BEGIN_SRC emacs-lisp (eval-when-compile (require 'use-package)) (require 'diminish) (require 'bind-key) #+END_SRC ** Custom file #+BEGIN_SRC emacs-lisp (setq custom-file "~/dotfiles/emacs/emacs-custom.el") (load custom-file) #+END_SRC * Org stuff ** Scratch buffer #+BEGIN_SRC emacs-lisp (setq org-src-preserve-indentation 't) (setq initial-major-mode 'org-mode) (setq initial-scratch-message "\ ,#+TITLE: Scratch buffer ,#+AUTHOR: Yorick van Pelt ") #+END_SRC ** modules #+BEGIN_SRC emacs-lisp #+END_SRC ** keybindings #+BEGIN_SRC emacs-lisp (bind-keys :map global-map ("C-c l" . org-store-link) ("C-c a" . org-agenda) ("C-c b" . org-iswitchb) ("C-c c" . org-capture)) #+END_SRC ** config #+BEGIN_SRC emacs-lisp (setq org-pretty-entities t) (setq org-startup-indented t) (setq org-src-fontify-natively t) (setq org-completion-use-ido t) (setq org-src-tab-acts-natively t) (setq org-ellipsis "⬎") (setq org-log-done t) (setq org-todo-keywords '((sequence "TODO(t)" "WAIT" "|" "DONE(d)" "CANCEL"))) (setq org-agenda-files (list "~/org/life.org" "~/org/ru.org" "~/org/symfo.org" "~/org/fiction.org" "~/serokell/serok.el/2019.org" "~/engineering/lumi.org" "~/serokell/serok.el/tasks.org")) (setq org-use-fast-todo-selection t) (setq org-directory "~/org") (defun org-file-path (filename) "Return the absolute address of an org file, given its relative name." (concat (file-name-as-directory org-directory) filename)) ; (setq org-inbox-file "~/org/inbox.org") (setq org-index-file (org-file-path "life.org")) (setq org-archive-location (concat (org-file-path "archive.org") "::* From %s")) (setq org-default-notes-file "~/org/refile.org") (setq org-agenda-todo-ignore-scheduled t) (setq org-capture-templates '(("l" "Today I learned" entry (file+datetree (org-file-path "til.org")) "* %?\n") ("u" "link" entry (file+headline org-index-file "check links") "* %^L\n") ("w" "Week review" entry (file+datetree (org-file-path "weeklog.org")) "* %?\n") ("t" "todo item" entry (file+headline org-index-file "todo") "* TODO %?\n"))) #+END_SRC #+RESULTS: | l | Today I learned | entry | (file+datetree (org-file-path til.org)) | * %? | | u | link | entry | (file+headline org-index-file check links) | * %^L | | w | Week review | entry | (file+datetree (org-file-path weeklog.org)) | * %? | | t | todo item | entry | (file+headline org-index-file todo) | * TODO %? | ** add templates #+BEGIN_SRC emacs-lisp (add-to-list 'org-structure-template-alist '("el" "#+BEGIN_SRC emacs-lisp\n?\n#+END_SRC")) (add-to-list 'org-structure-template-alist '("py" "#+BEGIN_SRC python\n?\n#+END_SRC")) (org-babel-do-load-languages 'org-babel-load-languages '((python . t) )) #+END_SRC #+RESULTS: ** org-bullets #+BEGIN_SRC emacs-lisp (use-package org-bullets :init (add-hook 'org-mode-hook 'org-bullets-mode t) :commands org-bullets-mode ) #+END_SRC ** TODO org-ref #+BEGIN_SRC emacs-lisp (use-package org-ref :disabled :config (setq org-ref-completion-library 'org-ref-ivy-cite)) #+END_SRC * Look ** Solarized #+BEGIN_SRC emacs-lisp ;; used to fix modeline (defvar after-load-theme-hook nil "Hook run after a color theme is loaded using `load-theme'.") (defun reload-solarized (event) (let ((theme (intern (concat "solarized-" (with-temp-buffer (insert-file-contents "~/dotfiles/color-scheme") (string-trim (buffer-string)) ))))) (load-theme theme t) (run-hooks 'after-load-theme-hook))) (use-package solarized :init (setq solarized-distinct-fringe-background t) (setq solarized-scale-org-headlines nil) :config (reload-solarized nil)) ;; auto-reload (use-package filenotify :after solarized :config (file-notify-add-watch "~/dotfiles/color-scheme" '(change) 'reload-solarized)) #+END_SRC ** Cleaner frames #+BEGIN_SRC emacs-lisp ;; toolbars are disabled in early-init.el (setq inhibit-startup-screen t) #+END_SRC ** hl-line #+BEGIN_SRC emacs-lisp (when window-system (global-hl-line-mode)) (show-paren-mode t) #+END_SRC ** live hex color previews from https://vickychijwani.me/nuggets-from-my-emacs-part-i/ #+BEGIN_SRC emacs-lisp ;; CSS color values colored by themselves ;; http://news.ycombinator.com/item?id=873541 (defvar hexcolor-keywords '(("#[abcdef[:digit:]]+" (0 (put-text-property (match-beginning 0) (match-end 0) 'face (list :background (match-string-no-properties 0))))))) (defun hexcolor-add-to-font-lock () (font-lock-add-keywords nil hexcolor-keywords)) (add-hook 'css-mode-hook 'hexcolor-add-to-font-lock) #+END_SRC ** doom-modeline #+BEGIN_SRC emacs-lisp (use-package doom-modeline :hook (after-init . doom-modeline-mode) (after-load-theme . doom-modeline-mode)) #+END_SRC * Feel #+BEGIN_SRC emacs-lisp (defalias 'yes-or-no-p 'y-or-n-p) (setq compilation-scroll-output t) #+END_SRC ** fix escape #+BEGIN_SRC emacs-lisp ; Map escape to cancel (like C-g)... (define-key isearch-mode-map [escape] 'isearch-abort) ;; isearch (define-key isearch-mode-map "\e" 'isearch-abort) ;; \e seems to work better for terminals (global-set-key [escape] 'keyboard-escape-quit) ;; everywhere else #+End_SRC ** Fix mouse wheel #+BEGIN_SRC emacs-lisp (setq mouse-wheel-scroll-amount '(1 ((shift) . 1))) ;; one line at a time (setq mouse-wheel-progressive-speed nil) ;; don't accelerate scrolling (setq mouse-wheel-follow-mouse 't) ;; scroll window under mouse (setq scroll-step 1) ;; keyboard scroll one line at a time (defun sfp-page-down (&optional arg) (interactive "^P") (setq this-command 'next-line) (next-line (- (window-text-height) next-screen-context-lines))) (put 'sfp-page-down 'isearch-scroll t) (put 'sfp-page-down 'CUA 'move) (defun sfp-page-up (&optional arg) (interactive "^P") (setq this-command 'previous-line) (previous-line (- (window-text-height) next-screen-context-lines))) (put 'sfp-page-up 'isearch-scroll t) (put 'sfp-page-up 'CUA 'move) (setq scroll-error-top-bottom t) #+END_SRC ** fix c-z #+BEGIN_SRC emacs-lisp (global-unset-key (kbd "C-z")) #+END_SRC ** Ivy #+BEGIN_SRC emacs-lisp (use-package ivy :init (setq ivy-height 10) (setq ivy-use-virtual-buffers t) (setq enable-recursive-minibuffers t) :config (ivy-mode t) :diminish :bind (("C-s" . swiper) ("C-c C-r" . ivy-resume) ("C-x b" . ivy-switch-buffer) ("" . ivy-resume))) #+END_SRC ** Counsel #+BEGIN_SRC emacs-lisp (use-package counsel :bind (("M-x" . counsel-M-x) ("C-x C-f" . counsel-find-file) ("C-h f" . counsel-describe-function) ("C-h v" . counsel-describe-variable))) #+END_SRC (global-set-key (kbd " l") 'counsel-find-library) (global-set-key (kbd " i") 'counsel-info-lookup-symbol) (global-set-key (kbd " u") 'counsel-unicode-char) ;; (global-set-key (kbd "C-c g") 'counsel-git) ;; (global-set-key (kbd "C-c j") 'counsel-git-grep) ;; (global-set-key (kbd "C-c k") 'counsel-ag) ;; (global-set-key (kbd "C-x l") 'counsel-locate) ;; (global-set-key (kbd "C-S-o") 'counsel-rhythmbox) ;; (define-key read-expression-map (kbd "C-r") 'counsel-expression-history) ** projectile #+BEGIN_SRC emacs-lisp (use-package projectile :init (setq projectile-mode-line "Projectile") :config (projectile-global-mode t) ) ;(use-package counsel-projectile)) #+END_SRC ** ggtags #+BEGIN_SRC emacs-lisp (use-package ggtags :bind ("M-." . ggtags-find-tag-dwim)) #+END_SRC ** intuitive window resize #+BEGIN_SRC emacs-lisp ;; intuitive window resizing (defun xor (b1 b2) (or (and b1 b2) (and (not b1) (not b2)))) (defun move-border-left-or-right (arg dir) "General function covering move-border-left and move-border-right. If DIR is t, then move left, otherwise move right." (interactive) (if (null arg) (setq arg 3)) (let ((left-edge (nth 0 (window-edges)))) (if (xor (= left-edge 0) dir) (shrink-window arg t) (enlarge-window arg t)))) (defun move-border-up-or-down (arg dir) "General function covering move-border-up and move-border-down. If DIR is t, then move up, otherwise move down." (interactive) (if (null arg) (setq arg 3)) (let ((top-edge (nth 1 (window-edges)))) (if (xor (= top-edge 0) dir) (shrink-window arg nil) (enlarge-window arg nil)))) (defun move-border-left (arg) (interactive "P") (move-border-left-or-right arg t)) (defun move-border-right (arg) (interactive "P") (move-border-left-or-right arg nil)) (defun move-border-up (arg) (interactive "P") (move-border-up-or-down arg t)) (defun move-border-down (arg) (interactive "P") (move-border-up-or-down arg nil)) ;; keybindings for window resizing (global-set-key (kbd "C-S-") 'move-border-left) (global-set-key (kbd "C-S-") 'move-border-right) (global-set-key (kbd "C-s-") 'move-border-up) (global-set-key (kbd "C-s-") 'move-border-down) #+END_SRC #+RESULTS: : move-border-down ** TODO i3-emacs ** Terminal #+BEGIN_SRC emacs-lisp (xterm-mouse-mode 1) (define-key local-function-key-map "\033[73;5~" [(control return)]) #+END_SRC * editing ** line numbers *** relative #+BEGIN_SRC emacs-lisp (use-package linum-relative :commands linum-relative-toggle) #+END_SRC *** enable globally #+BEGIN_SRC emacs-lisp (global-display-line-numbers-mode) #+END_SRC ** direnv #+BEGIN_SRC emacs-lisp (use-package direnv :config (direnv-mode)) #+END_SRC ** autocomplete #+BEGIN_SRC emacs-lisp (use-package company :diminish :hook (after-init . global-company-mode) :config ;; use copilot (delq 'company-preview-if-just-one-frontend company-frontends)) (use-package company-box :hook (company-mode . company-box-mode)) #+END_SRC ** Indentation #+BEGIN_SRC emacs-lisp (setq-default indent-tabs-mode nil) (setq-default tab-width 2) ; or any other preferred value (defvaralias 'c-basic-offset 'tab-width) (defvaralias 'cperl-indent-level 'tab-width) (defun yorick/copilot-tab (arg) (interactive "P") (or (copilot-accept-completion) (company-indent-or-complete-common arg))) (define-key prog-mode-map (kbd "") #'yorick/copilot-tab) #+END_SRC ** smart home key #+BEGIN_SRC emacs-lisp ;; "smart" home, i.e., home toggles b/w 1st non-blank character and 1st column (defun smart-beginning-of-line () "Move point to first non-whitespace character or beginning-of-line." (interactive "^") ; Use (interactive "^") in Emacs 23 to make shift-select work (let ((oldpos (point))) (back-to-indentation) (and (= oldpos (point)) (beginning-of-line)))) (global-set-key (kbd "C-a") 'smart-beginning-of-line) #+END_SRC ** git-gutter-fringe #+BEGIN_SRC emacs-lisp ;; (use-package git-gutter-fringe ;; :config (global-git-gutter-mode t)) #+END_SRC ** all-the-icons #+BEGIN_SRC emacs-lisp (use-package all-the-icons :commands all-the-icons-insert) #+END_SRC ** backups from [[https://www.emacswiki.org/emacs/BackupDirectory][emacs wiki]] #+BEGIN_SRC emacs-lisp (setq vc-make-backup-files t) (setq backup-by-copying t ; don't clobber symlinks backup-directory-alist '(("." . "~/.emacs.d/.saves")) ; don't litter my fs tree delete-old-versions t kept-new-versions 6 kept-old-versions 2 version-control t) ; use versioned backups #+END_SRC ** Undo-tree #+BEGIN_SRC emacs-lisp (use-package undo-tree :diminish undo-tree-mode :init ;; prevent .~undo-tree file pollution (setq undo-tree-auto-save-history nil) :config (global-undo-tree-mode)) #+END_SRC ** Evil #+BEGIN_SRC emacs-lisp (setq evil-want-C-i-jump nil) (use-package evil :config (evil-mode t) (evil-set-undo-system 'undo-tree) (define-key evil-motion-state-map (kbd "") 'smart-beginning-of-line) ;; change cursor based on mode (add-hook 'evil-insert-state-entry-hook (lambda () (when (not (display-graphic-p)) (send-string-to-terminal "\033[5 q")))) (add-hook 'evil-normal-state-entry-hook (lambda () (when (not (display-graphic-p)) (send-string-to-terminal "\033[0 q")))) ) (use-package evil-mc :config (global-evil-mc-mode 1) :after evil) (use-package which-key :diminish :init (setq which-key-allow-evil-operators t) (setq which-key-show-operator-state-maps t) :config (which-key-mode 1) (which-key-setup-minibuffer)) ; do I need this? #+END_SRC #+RESULTS: : t *** evil-goggles #+BEGIN_SRC emacs-lisp (use-package evil-goggles :diminish :after evil :config (evil-goggles-mode) (evil-goggles-use-diff-faces)) #+END_SRC *** TODO [[https://github.com/emacs-evil/evil-surround][evil-surround]] *** TODO more evil bindings **** https://github.com/Somelauw/evil-org-mode/blob/master/doc/keythemes.org **** follow link with ret ** TODO multiple-cursors ** crdt #+BEGIN_SRC emacs-lisp (use-package crdt :commands (crdt-connect)) #+END_SRC #+RESULTS: ** copilot #+BEGIN_SRC emacs-lisp (use-package copilot :commands (copilot-login copilot-mode) :bind (:map copilot-mode-map ("TAB" . copilot-accept-completion))) #+END_SRC ** DONE fix clipboard on wayland #+BEGIN_SRC emacs-lisp (setq wl-copy-process nil) (defun wl-copy (text) (setq wl-copy-process (let ((default-directory "~")) (make-process :name "wl-copy" :buffer nil :command '("wl-copy" "-f" "-n") :noquery t :connection-type 'pipe))) (process-send-string wl-copy-process text) (process-send-eof wl-copy-process)) (defun wl-paste () (if (and wl-copy-process (process-live-p wl-copy-process)) nil (let ((default-directory "~")) (shell-command-to-string "wl-paste -n | tr -d '\r'")))) (setq interprogram-cut-function 'wl-copy) (setq interprogram-paste-function 'wl-paste) #+END_SRC #+RESULTS: : wl-paste * Tools ** Magit #+BEGIN_SRC emacs-lisp (use-package magit :bind (("C-c g" . magit-status) ("C-c C-g l" . magit-log-all))) (use-package forge :init (setq forge-topic-list-limit '(60 . 0)) :after magit) #+END_SRC ** weechat #+BEGIN_SRC emacs-lisp (use-package weechat :commands weechat-connect :init (setq weechat-more-lines-amount 100) (setq weechat-host-default "pennyworth.yori.cc") (setq weechat-mode-default "ssh -W localhost:%p %h") (setq weechat-modules '(weechat-button weechat-complete weechat-notifications)) ) #+END_SRC ** notmuch #+BEGIN_SRC emacs-lisp (use-package notmuch :bind (("C-c n" . notmuch))) #+END_SRC ** TODO https://github.com/mbork/beeminder.el * language-specific ** markdown #+BEGIN_SRC emacs-lisp (use-package markdown-mode :commands (markdown-mode gfm-mode) :mode (("README\\.md\\'" . gfm-mode) ("\\.md\\'" . markdown-mode) ("\\.markdown\\'" . markdown-mode)) :init (setq markdown-command "multimarkdown")) #+END_SRC ** org *** TODO spellchecking *** TODO disable linum on org mode *** TODO use org-cliplink ** lua #+BEGIN_SRC emacs-lisp (use-package lua-mode :commands (lua-mode) :mode (("\\.lua\\'" . lua-mode))) #+END_SRC ** nix #+BEGIN_SRC emacs-lisp (defun nix-flake-current-dir () (interactive) (let ((default-directory (projectile-project-root))) (nix-flake (projectile-project-root)))) (use-package nix-mode :commands (nix-mode nix-flake) :bind (("C-c f" . nix-flake-current-dir)) :mode (("\\.nix\\'" . nix-mode))) #+END_SRC ** lsp #+BEGIN_SRC emacs-lisp (use-package lsp-mode :init (setq lsp-keymap-prefix "C-c s") :hook (lsp-mode . lsp-enable-which-key-integration) :commands (lsp lsp-deferred)) (use-package lsp-ivy :commands lsp-ivy-workspace-symbol :after lsp) (use-package lsp-ui :commands lsp-ui-mode :after lsp) (setq gc-cons-threshold 100000000) (setq read-process-output-max (* 1024 1024 3)) ;; 3mb #+END_SRC #+RESULTS: : 3145728 ** haskell #+BEGIN_SRC emacs-lisp (load-library "haskell-mode-autoloads") ;; (use-package intero ;; :config (add-hook 'haskell-mode-hook 'intero-mode) ;; ) #+END_SRC *** TODO intero / haskell mode [[https://wiki.haskell.org/Emacs]] ** rust #+BEGIN_SRC emacs-lisp (use-package rust-mode :commands (rust-mode) :mode (("\\.rs\\'" . rust-mode))) #+END_SRC ** terraform-mode #+BEGIN_SRC emacs-lisp (use-package terraform-mode :commands (terraform-mode) :mode (("\\.tf\\'" . terraform-mode))) #+END_SRC ** vue #+BEGIN_SRC emacs-lisp (use-package vue-mode :commands (vue-mode) :mode (("\\.vue\\'" . vue-mode))) #+END_SRC ** reason #+BEGIN_SRC emacs-lisp (use-package reason-mode :commands (reason-mode) :mode (("\\.re\\'" . reason-mode))) #+END_SRC ** TODO proof-general * Inspiration ** [[https://github.com/hrs/dotfiles/blob/master/emacs/.emacs.d/configuration.org][hrs]] ** [[https://github.com/angrybacon/dotemacs][angrybacon]] ** [[https://github.com/hlissner/.emacs.d][doom]] ** [[https://gist.github.com/fmap/b0e89549d43c4cc0d90c14579e366eb3][fmap]] ** [[https://github.com/muflax-scholars/emacs.d][muflax]] ** [[https://github.com/jwiegley/dot-emacs/blob/master/init.el][jwiegly]]