#+TITLE: My configuration #+AUTHOR: Andrew Guschin #+PROPERTY: tangle yes * Basic settings This replaces default Emacs welcome screen with custom simple splash. This is based on some combination of https://github.com/rougier/emacs-splash and splash screen of https://github.com/rougier/nano-emacs. Big thanks to original author, but I wanted simple splash screen that doesn't disappear after I start pressing buttons. I also want for this splash screen to be able to respond to window size changes, but for now I like it as it is. #+BEGIN_SRC emacs-lisp (setq inhibit-startup-screen t) (setq inhibit-startup-message t) (setq inhibit-startup-echo-area-message t) (setq initial-scratch-message nil) (setq initial-buffer-choice nil) (load-file (expand-file-name "scratch-splash.el" user-emacs-directory)) (add-hook 'window-setup-hook 'scratch-splash) #+END_SRC This starts with some settings that are useful for already initialized configuration. I set up theme first thing so that the default one doesn't blind me at night. Also set up the correct font. #+BEGIN_SRC emacs-lisp ;; (when (package-installed-p 'ayu-theme) ;; (load-theme 'ayu-grey t)) (when (package-installed-p 'kaolin-themes) (load-theme 'kaolin-galaxy t) ;; (load-theme 'kaolin-breeze t) ) (add-to-list 'default-frame-alist '(font . "FiraCode Nerd Font 15")) #+END_SRC Menu bar looks okay in macOS because it's contained in global menu bar of OS for focused window. I'd say that bar without application's menu looks strange on macOS. On linux menu is attached to every client, so this doesn't look that good, so it better be turned off. #+BEGIN_SRC emacs-lisp (when (eq system-type 'gnu/linux) (menu-bar-mode -1)) (when (eq system-type 'darwin) (menu-bar-mode t)) #+END_SRC Disable more of unneeded app decorations (now for all OSes), enable some niceties, and disable several annoying things. Looks like pixel scrolling works only on macOS. #+BEGIN_SRC emacs-lisp (tool-bar-mode -1) (scroll-bar-mode -1) (toggle-scroll-bar -1) (xterm-mouse-mode t) (buffer-face-mode t) (pixel-scroll-precision-mode t) (setq frame-resize-pixelwise t) (setq-default fill-column 100) (global-display-fill-column-indicator-mode t) (global-set-key (kbd "C-") 'ignore) (global-set-key (kbd "C-") 'ignore) (setq ring-bell-function 'ignore) #+END_SRC #+BEGIN_SRC emacs-lisp (setq-default indent-tabs-mode nil) (setq-default tab-width 8) (setq-default c-default-style "bsd") (setq-default c-basic-offset 4) #+END_SRC #+BEGIN_SRC emacs-lisp (defun my-backup-file-name (fpath) "Return a new file path of a given file path. If the new path's directories does not exist, create them." (let* ((backupRootDir (expand-file-name "backup" user-emacs-directory)) (filePath (replace-regexp-in-string "[A-Za-z]:" "" fpath )) ; remove Windows driver letter in path, for example, “C:” (backupFilePath (replace-regexp-in-string "//" "/" (concat backupRootDir filePath "~") ))) (make-directory (file-name-directory backupFilePath) (file-name-directory backupFilePath)) backupFilePath)) (setq make-backup-file-name-function 'my-backup-file-name) #+END_SRC I don't want this autogenerated code to be added to main config file automatically. Now it will be placed in its own file and won't annoy me. #+BEGIN_SRC emacs-lisp (setq custom-file (expand-file-name "custom.el" user-emacs-directory)) (load custom-file) #+END_SRC * Package management Several packages are not present in GNU ELPA, so I need to add MELPA archive. #+BEGIN_SRC emacs-lisp (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) #+END_SRC For package management I use use-package with always-ensure flag turned on. That way on new installs this config bootstraps itself. #+BEGIN_SRC emacs-lisp (unless (package-installed-p 'use-package) (package-refresh-contents) (package-install 'use-package)) (setq-default use-package-always-ensure t) (setq-default use-package-always-defer t) (require 'use-package) #+END_SRC * Plugins ** Theme Install some nice theme. It is loaded in the beginning of the config, so after bootstrapping emacs should be restarted. Or this theme can be loaded by hand... #+BEGIN_SRC emacs-lisp (use-package ayu-theme) (use-package kaolin-themes) (use-package auto-dark :custom (auto-dark-themes '((ayu-grey) (kaolin-breeze)))) #+END_SRC ** Small plugins #+BEGIN_SRC emacs-lisp (use-package adoc-mode) (use-package docker :bind ("C-c d" . docker)) (use-package vterm) (use-package origami :hook ((prog-mode . origami-mode))) (use-package diff-hl :hook ((text-mode . diff-hl-mode) (prog-mode . diff-hl-mode) (dired-mode . diff-hl-dired-mode))) (use-package benchmark-init) #+END_SRC ** evil-mode Emacs is great operating system that doesn't have good editor. It is useful to enable relative numbering of lines in normal state, for easier use of motions. But in insert mode it is not so useful, so this turns on regular numbering for this mode. But in some major modes evil shadows their keymaps, so I set their initial state to emacs's keymaps. #+BEGIN_SRC emacs-lisp (global-display-line-numbers-mode -1) (use-package evil :init (use-package undo-fu) (setq evil-undo-system 'undo-fu) (setq evil-want-keybinding nil) :config ;; (evil-set-initial-state 'dired-mode 'emacs) (evil-set-initial-state 'eshell-mode 'emacs) (evil-set-initial-state 'shell-mode 'emacs) (evil-set-initial-state 'buffer-menu-mode 'emacs) (define-key universal-argument-map (kbd "C-u") nil) (define-key evil-motion-state-map (kbd "C-u") 'evil-scroll-up) :hook ((prog-mode . evil-mode) (text-mode . evil-mode) (prog-mode . display-line-numbers-mode) (text-mode . display-line-numbers-mode) (evil-insert-state-entry . (lambda () (setq-local display-line-numbers t))) (evil-normal-state-entry . (lambda () (setq-local display-line-numbers 'relative))))) ;; (eval-after-load "dired" ;; '(progn ;; (dired-listing-switches ) ;; )) (use-package evil-collection :after evil :after dired :init (evil-collection-init '(dired)) (use-package dired-subtree) :config (evil-collection-define-key 'normal 'dired-mode-map "o" 'dired-subtree-toggle "c" 'dired-subtree-remove "i" nil "a" nil "A" nil )) #+END_SRC I use `C-u` keymap from vim very often, but in emacs it is bound to 'universal argument'. Universal argument is quite useful in some circumstances, but scrolling is used much more often. So this remaps universal argument to `C-f`, and scrolling to `C-u`. #+BEGIN_SRC emacs-lisp (define-key global-map (kbd "C-f") 'universal-argument) (define-key universal-argument-map (kbd "C-u") nil) (define-key universal-argument-map (kbd "C-f") 'universal-argument-more) (define-key global-map (kbd "C-u") 'kill-whole-line) (eval-after-load 'evil-maps '(progn (define-key evil-motion-state-map (kbd "C-f") nil) )) #+END_SRC ** magit Just great git client. I don't have any configurations for it yet. #+BEGIN_SRC emacs-lisp (use-package magit) (use-package magit-todos :after magit :config (magit-todos-mode 1)) #+END_SRC ** lisp packages #+BEGIN_SRC emacs-lisp (use-package paredit :hook (emacs-lisp-mode . paredit-mode)) (use-package rainbow-delimiters :hook (emacs-lisp-mode . rainbow-delimiters-mode)) #+END_SRC ** exec-path-from-shell I like to follow XDG Base Directory specification, and have many environment variables for tools, that don't use XDG directories. To use these tools within Emacs, I need to get some variables, that are set within my `.profile`. #+BEGIN_SRC emacs-lisp (use-package exec-path-from-shell :config (when (memq window-system '(mac ns x)) ;; I set up this variable, so that shell would know that it is being executed from emacs, and ;; not really interactively (setenv "EMACS" "emacs") (setq exec-path-from-shell-variables '("PATH" "CARGO_HOME" "RUSTUP_HOME" "GOPATH" "RYE_HOME" "NPM_CONFIG_USERCONFIG" "STACK_ROOT" "GHCUP_USE_XDG_DIRS")) (exec-path-from-shell-initialize))) #+END_SRC ** LSP Configuration for some languages that I used in Emacs. Not all the languages that I used, but most recent ones. If I decide to try something new, or open some old project in Emacs, this config (probably) will be updated. #+BEGIN_SRC emacs-lisp (setq major-mode-remap-alist '(;; (typescript-mode . typescript-ts-mode) (rust-mode . rust-ts-mode) )) (use-package lsp-mode :init ;; set prefix for lsp-command-keymap (few alternatives - "C-l", "C-c l") (setq lsp-keymap-prefix "C-l") (use-package company) (use-package rust-mode) (use-package go-mode) (use-package projectile) (use-package lsp-java :custom (setq lsp-java-server-install-dir (concat (getenv "HOME") "/.local/share/jdtls/"))) ;; (use-package typescript-mode) (use-package lsp-tailwindcss :after lsp-mode :init (setq lsp-tailwindcss-add-on-mode t)) (use-package web-mode) ;; if you want which-key integration ;;(lsp-mode . lsp-enable-which-key-integration)) :hook ((lsp-mode . company-mode) (rust-ts-mode . lsp) (rust-ts-mode . projectile-mode) (go-mode . lsp) (java-mode . lsp) (c++-mode . lsp) ;; (web-mode . lsp) ;; (typescript-mode . add-node-modules-path) ;; (typescript-mode . web-mode) (typescript-ts-mode . lsp) (typescript-ts-mode . projectile-mode) (typescript-ts-mode . prettier-js-mode) (tsx-ts-mode . lsp) (tsx-ts-mode . projectile-mode) (tsx-ts-mode . prettier-js-mode) ;; (typescript-mode . (lambda () ;; (projectile-mode) ;; (lsp) ;; )) ) :commands lsp :custom (lsp-clients-typescript-prefer-use-project-ts-server 1) (lsp-clients-typescript-tls-path (concat (projectile-project-root) "node_modules/.bin/typescript-language-server")) ;; () ;; Saved in case I use vue.js with lsp some other time ;; :custom ;; (lsp-clients-typescript-plugins ;; (vector (list :name "@vue/typescript-plugin" ;; :location (concat (getenv "BUN_INSTALL") ;; "/install/global/node_modules/@vue/typescript-plugin") ;; :languages (vector "typescript" "javascript" "vue")))) ) (use-package flycheck :hook (after-init . #'global-flyckeck-mode)) (use-package lsp-ui :commands lsp-ui-mode) (use-package helm-lsp :commands helm-lsp-workspace-symbol) #+END_SRC Web development tools need more configuration, than most other languages. So all of this configuration is done inside web-mode, because most web projects are used with multiple language servers (most of the time with multiple for single buffer, even). #+BEGIN_SRC emacs-lisp ;; (use-package web-mode ;; :init ;; (use-package prettier-js) ;; (use-package vue-mode) ;; (use-package svelte-mode) ;; (use-package lsp-tailwindcss ;; :after web-mode ;; :init (setq lsp-tailwindcss-add-on-mode t)) ;; :after lsp-mode ;; :hook ((web-mode . prettier-js-mode)) ;; :mode (("\\.ts\\'" . web-mode) ;; ("\\.js\\'" . web-mode) ;; ("\\.vue\\'" . web-mode) ;; ("\\.tsx\\'" . web-mode) ;; ("\\.jsx\\'" . web-mode)) ;; :config ;; (setq web-mode-markup-indent-offset 2) ;; (setq web-mode-css-indent-offset 2) ;; (setq web-mode-code-indent-offset 2) ;; (setq web-mode-script-padding 0) ;; (setq web-mode-style-padding 0) ;; (setq web-mode-block-padding 0) ;; (setq web-mode-content-types-alist ;; '(("jsx" . "\\.js[x]?\\'")))) ;; (use-package add-node-modules-path :commands add-node-modules-path) #+END_SRC ** Tree-sitter #+BEGIN_SRC emacs-lisp (use-package treesit :ensure nil :config (setq-default treesit-language-source-alist '((tsx . ("https://github.com/tree-sitter/tree-sitter-typescript" "master" "tsx/src")) (typescript . ("https://github.com/tree-sitter/tree-sitter-typescript" "master" "typescript/src")) )) (treesit-install-language-grammar 'tsx) (treesit-install-language-grammar 'typescript) ) ;; (use-package treesit-auto ;; :config ;; (add-to-list 'treesit-auto-recipe-list ;; (make-treesit-auto-recipe ;; :lang 'tsx ;; :ts-mode 'tsx-ts-mode ;; :url "https://github.com/tree-sitter/tree-sitter-typescript" ;; :source-dir "tsx/src" ;; :ext "\\.tsx\\'")) ;; (setq treesit-auto-langs '(tsx)) ;; ) ;; (use-package tree-sitter ;; :init ;; (use-package tree-sitter-langs) ;; :hook (tree-sitter-after-on . tree-sitter-hl-mode)) #+END_SRC ** Spelling #+BEGIN_SRC emacs-lisp (setq ispell-program-name "hunspell") (add-hook 'text-mode-hook 'flyspell-mode) (add-hook 'prog-mode-hook 'flyspell-prog-mode) (add-hook 'text-mode-hook 'ispell-minor-mode) (add-hook 'prog-mode-hook 'ispell-minor-mode) #+END_SRC