;;; init.el --- tastytea's Emacs init file. ;; Time-stamp: <2019-10-14T10:08:54+00:00> ;;; Commentary: ;; Requires at least Emacs 26. Most of it will probably work with Emacs 24 and ;; above though. ;;; Code: ;;;;;;;;;;;;;;;;;;;; Packages ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (require 'package) (add-to-list 'package-archives '("melpa-stable" . "https://stable.melpa.org/packages/") t) (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) (setq package-archive-priorities '( ("melpa-stable" . 30) ("gnu" . 20) ("melpa" . 10) )) ;; Workaround for 26.2 (defvar gnutls-algorithm-priority "NORMAL:-VERS-TLS1.3") (package-initialize) ;; Add path for custom packages. (add-to-list 'load-path "~/.emacs.d/custom-packages/") ;; Install use-package if necessary. (unless (package-installed-p 'use-package) (package-refresh-contents) (package-install 'use-package)) (eval-when-compile (require 'use-package)) ;; Always install packages if they are not present. (require 'use-package-ensure) (setq use-package-always-ensure t) ;; autocompile files as needed. (use-package auto-compile :custom ;; Use uncompiled file if it is newer than the compiled one. (load-prefer-newer t) :config (auto-compile-on-load-mode)) ;; ;; Benchmark for startup-file. ;; (use-package benchmark-init ;; :config ;; ;; To disable collection of benchmark data after init is done. ;; (add-hook 'after-init-hook 'benchmark-init/deactivate)) ;; Update packages if at least 7 days have passed. (use-package auto-package-update :custom (auto-package-update-delete-old-versions t) (auto-package-update-interval 7) (auto-package-update-hide-results nil) :config (auto-package-update-maybe)) ;;;;;;;;;;;;;;;;;;;; Global variables ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Determine if we run on a slow computer. (defvar slow-computer nil) (if (member (system-name) '("steuerbeamter" "azimuth" "localhost")) (setq slow-computer t)) ; localhost is schnibble ;; Show manpages and error messages from compilers in English. (setenv "LANG" "en_US.utf8") ;;;;;;;;;;;;;;;;;;;; Configure some essential things ;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Set garbage collection threshold to 100 MiB (or 20 MiB) to speed up init. ;; It is reset at the end of the file. (if slow-computer (setq gc-cons-threshold (* 20 1024 1024)) (setq gc-cons-threshold (* 100 1024 1024))) (use-package emacs :init ;; Banish customizations to another file. (setq custom-file (concat user-emacs-directory "custom.el")) :custom (inhibit-startup-screen t) (inhibit-startup-echo-area-message t) (backup-directory-alist ; Save backups in ~/.emacs.d/backups/. `(("." . ,(concat user-emacs-directory "backups")))) (delete-old-versions t) ; Delete old backups. (kept-new-versions 6) ; Keep 6 newest backups. (backup-by-copying t) ; Copy to backup folder. (version-control t) ; Append version numbers to file names. (auto-save-file-name-transforms ; Save auto-saves in ~/.emacs.d/backups/. `((".*" ,(concat user-emacs-directory "backups/") t))) (create-lockfiles nil) ; Don't create lockfiles (.#). (user-full-name "tastytea") (user-mail-address "tastytea@tastytea.de") (mouse-wheel-scroll-amount '(1 ((shift) . 1))) ; Scroll 1 line at a time. ;; Paste text where the cursor is, not where the mouse is. (mouse-yank-at-point t) (initial-scratch-message nil) ; Make scratch buffer empty, (initial-major-mode 'markdown-mode) ; and select mode. (require-final-newline t) ; Always add newline at end of file. (recentf-max-saved-items 40) ; Keep this number of buffers in history. (fill-column 80) ; Documents are 80 chars wide by default. :config (defalias 'yes-or-no-p 'y-or-n-p) ; Just type y/n instead of yes/no. (defun my/set-fill-column-100 () "Set fill-column to 100." (set-fill-column 100)) (defun my/set-fill-column-120 () "Set fill-column to 120." (set-fill-column 120)) ;; kill-region (cut) and kill-ring-save (copy) act on the current line if no ;; text is visually selected. ;; https://www.emacswiki.org/emacs/WholeLineOrRegion (put 'kill-ring-save 'interactive-form '(interactive (if (use-region-p) (list (region-beginning) (region-end)) (list (line-beginning-position) (line-beginning-position 2))))) (put 'kill-region 'interactive-form '(interactive (if (use-region-p) (list (region-beginning) (region-end)) (list (line-beginning-position) (line-beginning-position 2))))) (delete-selection-mode t) ; Delete selection when you start to write. (savehist-mode t) ; Save minibuffer history. (global-auto-revert-mode t) ; Auto-revert file if changed on disk. (defvar my/skippable-buffers '("^\\*" "^magit[:-]") "Buffer names ignored by `next-buffer' and `previous-buffer'.") (defvar my/never-skippable-buffers '("^\\*scratch\\*$" "^\\*Easy-hugo\\*$") "Buffer names never ignored by `next-buffer' and `previous-buffer'.") (defun my/buffer-predicate (buffer) "Returns nil if buffer-name matches expression in `my/skippable-buffers'." (catch 'return ;; Return t if buffer-name is on never-skippable list. (dolist (expression my/never-skippable-buffers) (if (string-match expression (buffer-name buffer)) (throw 'return t) )) ;; Return nil if buffer-name is on skippable list. (dolist (expression my/skippable-buffers) (if (string-match expression (buffer-name buffer)) (throw 'return nil) )) t)) (set-frame-parameter nil 'buffer-predicate 'my/buffer-predicate) :hook (text-mode . auto-fill-mode) ; Enable word-wrapping at fill-column. ) ;; Save cursor position. (use-package saveplace :config (save-place-mode t)) ;; Show and select buffers. (use-package bs :bind ("C-x C-b" . bs-show)) ;;;;;;;;;;;;;;;;;;;; Keybindings ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (use-package bind-key :init (defun my/delete-word (arg) "Delete characters forward until encountering the end of a word. With argument, do this that many times." (interactive "p") (if (use-region-p) (delete-region (region-beginning) (region-end)) (delete-region (point) (progn (forward-word arg) (point))))) (defun my/backward-delete-word (arg) "Delete characters backward until encountering the end of a word. With argument, do this that many times." (interactive "p") (my/delete-word (- arg))) :config (bind-keys ;; Reduce whitespace around cursor to 0 or 1, according to context. ("C-S-" . fixup-whitespace) ;; Scroll without moving the cursor. ("M-" . scroll-up-line) ("M-" . scroll-down-line) ;; Delete words without storing them in the kill buffer. ("C-" . my/delete-word) ("C-" . my/backward-delete-word) ;; Switch buffers. ("M-" . previous-buffer) ("M-" . next-buffer) ;; Switch windows. ("M-S-" . previous-multiframe-window) ("M-S-" . next-multiframe-window) ("s-" . previous-multiframe-window) ; s = Super ("s-" . next-multiframe-window) ;; Switch between header and implementation. ("C-:" . ff-find-other-file) )) ;;;;;;;;;;;;;;;;;;;; Programming / general ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Online documentation mode. (use-package eldoc :hook (prog-mode . turn-on-eldoc-mode)) ;; Syntax checking with many plugins. (unless slow-computer (use-package flycheck :defer nil :custom (flycheck-cppcheck-checks '("style" "warning" "information")) :config (global-flycheck-mode) ;; (setq flycheck-check-syntax-automatically '(save new-line mode-change)) (flycheck-add-mode 'html-tidy 'web-mode) (flycheck-add-mode 'css-csslint 'web-mode) :bind ("" . flycheck-previous-error) ("" . flycheck-next-error) ("" . flycheck-list-errors) ) (use-package flycheck-clang-tidy :pin melpa :after (flycheck projectile lsp-ui) :if (executable-find "clang-tidy") :config (defun my/clang-tidy-off () "Disable c/c++-clang-tidy." (when (or (eq major-mode 'c++-mode) (eq major-mode 'c-mode)) (add-to-list 'flycheck-disabled-checkers 'c/c++-clang-tidy)) ) (defun my/clang-tidy-on () "Enable c/c++-clang-tidy." (when (or (eq major-mode 'c++-mode) (eq major-mode 'c-mode)) (setq-local flycheck-disabled-checkers (remove 'c/c++-clang-tidy flycheck-disabled-checkers))) ) (defun my/flycheck-clang-tidy-setup () (flycheck-clang-tidy-setup) ;; Run clang-tidy after the lsp-ui checker. (when lsp-mode (unless (equal (flycheck-get-next-checker-for-buffer 'lsp-ui) 'c/c++-clang-tidy) (flycheck-add-next-checker 'lsp-ui '(warning . c/c++-clang-tidy))))) :hook (flycheck-mode . my/flycheck-clang-tidy-setup) (first-change . my/clang-tidy-off) ; Disable when file is modified. (before-save . my/clang-tidy-on) ; Enable if file is saved. ) ) ;; Autocompletion mode with many plugins. (unless slow-computer (use-package company :custom ;; Show suggestions after entering one character. (company-minimum-prefix-length 1) ;; Wrap around at end/beginning of list. (company-selection-wrap-around t) ;; Align annotation to the right border. (company-tooltip-align-annotations t) :bind (:map company-active-map ("" . nil) ; Disable completion on return. ("RET" . nil) ; https://emacs.stackexchange.com/a/13290 ("" . company-complete-selection) ; Make tab work in lists. ("TAB" . company-complete-selection)) :hook (after-init . global-company-mode))) ;; Fuzzy autocompletion for company. (use-package company-flx :after company :config (company-flx-mode +1)) (use-package company-statistics :after company :hook (after-init . company-statistics-mode)) ;; Documentation popups for completions. (use-package company-quickhelp :config (company-quickhelp-mode) ) ;; Set default indentation. (setq-default indent-tabs-mode nil tab-width 4) ;; Guess indentation (c-basic-offset). (use-package dtrt-indent :hook (prog-mode . dtrt-indent-mode)) ;; Automatic project management. (unless slow-computer (use-package projectile :after (neotree ivy) :init ;; (defun my/projectile-kill-buffers () ;; "Kill project buffers and hide neotree." ;; (interactive) ;; (projectile-kill-buffers) ;; (neotree-hide)) (defvar my/cmake-compile-command ; cmake command for compiling with 1 (concat "cmake --build . -- -j" ; core less than available. (substring (shell-command-to-string "nproc --ignore=1") 0 -1))) :custom (projectile-project-compilation-dir "build") (projectile-switch-project-action 'neotree-projectile-action) (projectile-project-configure-cmd "cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ -GUnix\\ Makefiles ..") (projectile-completion-system 'ivy) :config (setq projectile-project-compilation-cmd (concat my/cmake-compile-command " && cd tests && ctest -Q")) (projectile-mode +1) ;; Mark variable as safe. This prevents prompts when using .dir-locals.el. (put 'projectile-project-compilation-cmd 'safe-local-variable #'stringp) (put 'projectile-project-configure-cmd 'safe-local-variable #'stringp) :bind ("C-c p" . 'projectile-command-map) )) ;; Highlight TODO, FIXME, NOTE and so on. (use-package hl-todo :bind (:map hl-todo-mode-map ("C-c t" . hl-todo-occur)) :hook (prog-mode . hl-todo-mode)) ;; Better commenting. (use-package smart-comment :bind ("C-x c" . smart-comment)) ;; Toggle betweeen beginning/end of line and beginning/end of code. (use-package mwim :bind ("" . mwim-beginning-of-line-or-code) ("" . mwim-end-of-line-or-code)) ;; Auto-type closing brackets. (electric-pair-mode t) ;; Fold code. (use-package fold-dwim :bind ("C-x t" . fold-dwim-toggle) :hook (prog-mode . hs-minor-mode)) (use-package hl-indent :custom-face (hl-indent-face ((t (:inherit hl-indent-face ; Reversed whitespace. :background "gray18" :foreground "#1c1e1f" )))) :hook (prog-mode . hl-indent-mode) ) ;; Tries to find points of interest and jumps to them. (use-package imenu-anywhere :after ivy :bind ("M-i" . ivy-imenu-anywhere) ) (use-package highlight-doxygen :custom-face (highlight-doxygen-comment ((t (:inherit font-lock-comment-face :foreground "#667788")))) (highlight-doxygen-code-block ((t (:inherit highlight-doxygen-comment-face)))) :hook (prog-mode . highlight-doxygen-mode)) (use-package dumb-jump :custom (dumb-jump-selector 'ivy) :bind ("M-." . dumb-jump-go) ) ;; Support .editorconfig files. (use-package editorconfig :config (editorconfig-mode 1)) ;; A template system. (use-package yasnippet :config (yas-reload-all) :hook (prog-mode . yas-minor-mode) ) ;; Install snippet-collection but don't use it. (use-package yasnippet-snippets :defer t :config (setq yasnippet-snippets-dir "") ) (use-package smerge :ensure nil ; builtin :bind ("C-" . smerge-prev) ("C-" . smerge-next) ("C-" . smerge-resolve) ) ;;;;;;;;;;;;;;;;;;;; Programming / C++ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Set coding style. (use-package cc-mode :ensure nil ; Included in Emacs. :config (c-add-style "tastytea" '("bsd" (c-basic-offset . 4) (c-offsets-alist . ((innamespace . [0]))))) ; Don't indent in namespaces. (c-add-style "tastytea-legacy" ; For old code. '("bsd" (c-basic-offset . 4))) (defun my/get-builddir () (if (member 'projectile-mode minor-mode-list) (concat (projectile-project-root) "build") nil)) :custom (c-default-style "tastytea")) (unless slow-computer ;; Client for Language Server Protocol servers. (use-package lsp-mode :if (executable-find "clangd") :custom (lsp-prefer-flymake nil) ; Disable flymake. (lsp-auto-guess-root t) ; Don't ask for project root. (lsp-clients-clangd-args '("-compile-commands-dir=build")) (lsp-eldoc-render-all t) ; Display all eldoc information. :hook (c++-mode . lsp) (c-mode . lsp) ) ;; Eye-candy and flycheck support for lsp-mode. (use-package lsp-ui :after (lsp-mode flycheck) :custom (lsp-ui-sideline-enable nil) ; Do not insert doc into buffer. (lsp-ui-doc-include-signature t) ; Include signature in doc popup. (lsp-ui-doc-enable nil) ; Disable doc popup. :bind (:map lsp-ui-mode-map ("M-." . lsp-ui-peek-find-definitions)) :hook (lsp-mode . lsp-ui-mode) ) ;; Completions with lsp-mode. (use-package company-lsp :after (lsp-mode company) :config (push 'company-lsp company-backends) ) ) ; unless slow-computer ;; Highlighting and indentation for CMake. (use-package cmake-mode ;; The CMake ebuild installs and activates the mode. :unless (string-match-p "gentoo" operating-system-release) :mode ("CMakeLists\\.txt\\'" . cmake-mode) ("\\.cmake\\'" . cmake-mode) ) ;; ;; Doesn't work, find bug later. ;; ;; Advanced highlighting for CMake. ;; (use-package cmake-font-lock ;; :after cmake-mode ;; :hook ;; (cmake-mode . 'cmake-font-lock-activate) ;; ) ;; CMake reference. (use-package eldoc-cmake :after cmake-mode :hook (cmake-mode . eldoc-cmake-enable) ) ;; GUI for gdb and other debuggers. (use-package realgud :after cc-mode :config (defun my/launch-gdb () "Load realgud and launch gdb." (interactive) (load-library "realgud") (realgud:gdb)) :bind (:map c-mode-base-map ("C-c g" . my/launch-gdb) ("M-" . nil) ; Disabled, because I use them for scrolling. ("M-" . nil))) ; ^ FIXME: Does not work. ^ ;; Extra highlighting. (use-package modern-cpp-font-lock :hook (c++-mode . modern-c++-font-lock-mode) ) ;; Add #include directives for missing symbols. (use-package clang-include-fixer :ensure nil ; Installed by clang. :if (executable-find "clang-include-fixer") :init (defvar my/llvm-path (concat (file-name-directory (executable-find "clang-include-fixer")) "..")) (add-to-list 'load-path (concat my/llvm-path "/share/clang")) :config (defun my/clang-find-all-symbols () "Invokes run-find-all-symbols.py." (interactive) (shell-command (concat "cd " (my/get-builddir) " && " my/llvm-path "/share/clang/run-find-all-symbols.py" " -b " my/llvm-path "/bin/find-all-symbols"))) (defun my/clang-include-fixer () "Invoke clang-include-fixer with -p=build." (interactive) (message (concat "Calling the include fixer. " "This might take some seconds. Please wait.")) (clang-include-fixer--start #'clang-include-fixer--add-header "-output-headers" (concat "-p=" (my/get-builddir)))) :bind ("C-x M-i" . my/clang-include-fixer) ) ;;;;;;;;;;;;;;;;;;;; Appearance ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Hide toolbar. (tool-bar-mode -1) ;; Put scrollbar to the right side. (if (display-graphic-p) (set-scroll-bar-mode 'right)) ;; Icons (required by doom). (use-package all-the-icons :config (unless (file-exists-p "~/.local/share/fonts/all-the-icons.ttf") (all-the-icons-install-fonts t))) ;; Themes for doom-modeline. (unless slow-computer (use-package doom-themes :after all-the-icons :custom-face (font-lock-comment-face ((t (:inherit font-lock-comment-face :foreground "#667755")))) :config (load-theme 'doom-molokai t) )) ;; Neat modeline. (use-package doom-modeline :after all-the-icons :init (column-number-mode t) ; Show column numbers in modeline. (size-indication-mode) ; Buffer size display in the modeline. :config (setq doom-modeline-minor-modes nil ;; doom-modeline-buffer-file-name-style 'relative-to-project doom-modeline-buffer-file-name-style 'truncate-except-project) :hook (after-init . doom-modeline-mode)) ;; Miscellaneous visual improvements. (add-to-list 'default-frame-alist ; Set default font. '(font . "Source Code Pro-10")) (global-hl-line-mode t) ; Highlight current line. (show-paren-mode t) ; Visualize matching parens. ;; Show line numbers on the left side of the buffer. (use-package display-line-numbers :if (>= emacs-major-version 26) :config (global-display-line-numbers-mode)) ;;;;;;;;;;;;;;;;;;;; Misc ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Manual pages. (use-package man :defer t :custom (Man-width fill-column) :custom-face (Man-overstrike ((t (:inherit font-lock-type-face :bold t)))) (Man-underline ((t (:inherit font-lock-keyword-face :underline t)))) :bind ("" . man) ) (use-package woman :custom (woman-fill-frame t) :config (defun my/woman-topic-at-point () "Call woman and use the word at point as topic if it exists." (interactive) (let ((woman-use-topic-at-point t)) (woman)) ) ;; Open manpages in new window. (setq display-buffer-alist '(("\\`\\*WoMan" display-buffer-pop-up-window))) :bind ("C-" . my/woman-topic-at-point) ) ;; Show directory tree in a window. (use-package neotree :pin melpa ; <= 0.5.2 changes directory on file switch. :demand t :after all-the-icons :custom (neo-smart-open t) (neo-show-updir-line t) ; Disabled by doom-themes? (neo-window-width 40) (neo-show-hidden-files t) (neo-theme 'icons) :bind ("" . neotree-show) ("C-" . neotree-hide) ) ;; Git integration. (use-package git-commit :pin melpa) (unless slow-computer (use-package magit :pin melpa :custom (magit-diff-refine-hunk 'all) ; Show word-granularity differences. :config (defun my/magit-kill-buffers (arg) "Restore window configuration and kill all Magit buffers." (interactive) (let ((buffers (magit-mode-get-buffers))) (magit-restore-window-configuration) (mapc #'kill-buffer buffers))) :bind ("C-x g" . magit-status) ("C-x M-g" . magit-dispatch) ) (use-package magit-todos :hook (magit-mode . magit-todos-mode) ) ) ;; Completion in many Emacs commands. (use-package ivy :custom (ivy-use-virtual-buffers t) (ivy-count-format "[%d/%d] ") (ivy-wrap t) :config (ivy-mode 1) :bind ("C-c C-r" . 'ivy-resume) (:map ivy-minibuffer-map ("M-" . ivy-previous-history-element) ("M-" . ivy-next-history-element) ) ) ;; Extensions for ivy (use-package counsel :bind ("C-x C-f" . 'counsel-find-file) ("M-x" . 'counsel-M-x) ) ;; Use icons in ivy. (use-package all-the-icons-ivy :after (all-the-icons counsel) :config (all-the-icons-ivy-setup) ) ;; More information in ivy mini-buffers. (use-package ivy-rich ;; all-the-icons-ivy would override the ivy-rich switch-buffer improvements. :after (all-the-icons-ivy counsel) :config (defun ivy-rich-switch-buffer-icon (candidate) (with-current-buffer (get-buffer candidate) (let ((icon (all-the-icons-icon-for-mode major-mode))) (if (symbolp icon) (all-the-icons-icon-for-mode 'fundamental-mode) icon)))) (ivy-rich-mode 1) (setcdr (assq t ivy-format-functions-alist) #'ivy-format-function-line) :custom ;; It seems I have to copy this whole list to get icons in switch-buffer. (ivy-rich-display-transformers-list (quote (ivy-switch-buffer (:columns ((ivy-rich-switch-buffer-icon :width 2) (ivy-rich-candidate (:width 30)) (ivy-rich-switch-buffer-size (:width 7)) (ivy-rich-switch-buffer-indicators (:width 4 :face error :align right)) (ivy-rich-switch-buffer-major-mode (:width 12 :face warning)) (ivy-rich-switch-buffer-project (:width 15 :face success)) (ivy-rich-switch-buffer-path (:width (lambda (x) (ivy-rich-switch-buffer-shorten-path x (ivy-rich-minibuffer-width 0.3)))))) :predicate (lambda (cand) (get-buffer cand))) counsel-M-x (:columns ((counsel-M-x-transformer (:width 40)) (ivy-rich-counsel-function-docstring (:face font-lock-doc-face)))) counsel-describe-function (:columns ((counsel-describe-function-transformer (:width 40)) (ivy-rich-counsel-function-docstring (:face font-lock-doc-face)))) counsel-describe-variable (:columns ((counsel-describe-variable-transformer (:width 40)) (ivy-rich-counsel-variable-docstring (:face font-lock-doc-face)))) counsel-recentf (:columns ((ivy-rich-candidate (:width 0.8)) (ivy-rich-file-last-modified-time (:face font-lock-comment-face))))))) ) ;; Better search. (use-package swiper :after ivy :bind ("C-s" . 'swiper) ) ;; Ruler with fill-column marker. (use-package ruler-mode :custom-face (ruler-mode-default ((t (:inherit default :background "gray18" :foreground "dim gray" :box nil)))) (ruler-mode-column-number ((t (:inherit ruler-mode-default :foreground "dark gray")))) :hook (find-file . (lambda () (ruler-mode 1))) (text-mode . (lambda () (ruler-mode 1))) ; For the scratch buffer. ) ;; Visualize whitespace. (use-package whitespace :after company :custom (whitespace-line-column nil) ; Set to fill-column. :config (delete 'newline-mark whitespace-style) ; Don't paint $ at eol. (delete 'lines whitespace-style) ; Don't mark whole overly long lines. (add-to-list 'whitespace-style 'lines-tail) ; Mark end of overly long lines. ;; Workaround to not show dots in popup menus. (defun my/whitespace-mode-enabled-p () (symbol-value 'whitespace-mode)) (defvar-local my/ws-enabled nil) ;; company: (defun my/on-off-whitespace-before-company(command) (when (string= "show" command) (setq-local my/ws-enabled (my/whitespace-mode-enabled-p)) (if my/ws-enabled (whitespace-mode -1))) (when (string= "hide" command) (if my/ws-enabled (whitespace-mode t)))) (advice-add 'company-call-frontends :before #'my/on-off-whitespace-before-company) ;; popup: (defadvice popup-create (before my/popup-suppress-whitespace-mode activate) "Suspend whitespace-mode while popups are visible." (setq-local my/ws-enabled (my/whitespace-mode-enabled-p)) (if my/ws-enabled (whitespace-mode -1))) (defadvice popup-delete (after my/popup-restore-whitespace-mode activate) "Restore whitespace-mode when all popups have closed." (if my/ws-enabled (whitespace-mode t))) (defun my/whitespace-mode-off () (whitespace-mode -1)) (if (display-graphic-p) (custom-set-faces '(whitespace-line ((t (:inherit whitespace-line :weight normal :foreground nil :background nil :box (:line-width 1 :color "dark red") ;; :underline (:color "dark red") ))))) (custom-set-faces ; else '(whitespace-line ((t (:inherit whitespace-line :background nil :underline t ))))) ) (defun my/ws-load-local-vars-first () "Loads local variables (fill-column) before enabling whitespace-mode." (hack-local-variables) (whitespace-mode) ) :bind ("C-x w" . whitespace-mode) :hook (prog-mode . my/ws-load-local-vars-first) (conf-mode . my/ws-load-local-vars-first) (text-mode . my/ws-load-local-vars-first) (lsp-ui-peek-mode . my/whitespace-mode-off) ; Dots in wrong color. :custom-face (whitespace-space ((nil :foreground "gray18"))) ) ;; Spell checking. (unless slow-computer (if (or (executable-find "aspell") (executable-find "hunspell") (executable-find "ispell")) (use-package flyspell :custom (ispell-dictionary "english") :config (defun my/toggle-flyspell () "Toggle flyspell-mode and run flyspell-buffer after activating." (interactive) (if (bound-and-true-p flyspell-mode) (flyspell-mode 0) (flyspell-mode) (flyspell-buffer))) (defun my/flyspell-german () "Set dictionary to german." (interactive) (ispell-change-dictionary "german")) :bind ("" . my/toggle-flyspell) (:map flyspell-mode-map ("C-;" . nil)) ; iedit needs C-;. :hook ;; Spellcheck comments. (prog-mode . flyspell-prog-mode) ;; Spellcheck text documents. (LaTeX-mode . my/flyspell-german) (LaTeX-mode . flyspell-mode) (adoc-mode . flyspell-mode) (markdown-mode . flyspell-mode) (git-commit-mode . flyspell-mode) :mode ("COMMIT_EDITMSG\\'" . flyspell-mode) ))) ; Multiple cursors. (use-package multiple-cursors :init (global-unset-key (kbd "M-")) :bind ("C-x M-m" . mc/edit-lines) ("M-" . mc/add-cursor-on-click)) ;; If 2 files have the same name, append directory name after the filename. (use-package uniquify :ensure nil ; Included in Emacs. :custom (uniquify-after-kill-buffer-p t) (uniquify-buffer-name-style 'post-forward) (uniquify-strip-common-suffix t)) ;; Delete old buffers. ;; https://www.emacswiki.org/emacs/CleanBufferList (use-package midnight :defer 10 :init (setq midnight-delay 30 ; 30 seconds after "midnight" midnight-period (* 2 60 60)) ; Clean every 2 hours. :custom (clean-buffer-list-delay-general 1) ; Clean normal bufs after 1d. (clean-buffer-list-delay-special (* 30 60)) ; Clean special bufs after 30m. :config (setq clean-buffer-list-kill-regexps ; Add these to special buffers. (nconc clean-buffer-list-kill-regexps '("\\`magit-?.*:" "\\.log\\'" "\\`\\*rdm\\*\\'" "\\`\\*Backtrace\\*\\'"))) (midnight-mode t)) ;; The string Time-stamp: <> in the first 8 lines of the file will be updated ;; with the current timestamp. (use-package time-stamp :custom (time-stamp-format "%:y-%02m-%02dT%02H:%02M:%02S+00:00") (time-stamp-time-zone t) ; Set to UTC until ISO 8601 is supported. :hook (before-save . time-stamp)) ;; Edit multiple regions in the same way simultaneously. (use-package iedit :bind ("C-;" . iedit-mode)) ;; Mode for writing blog posts with hugo. (use-package easy-hugo :if (string= (system-name) "ventiloplattform") :custom (easy-hugo-basedir "~/Projekte/www/blog.tastytea.de/") (easy-hugo-url "https://blog.tastytea.de") (easy-hugo-previewtime "7200") ; 2 hours. (easy-hugo-postdir "content/posts") (easy-hugo-default-ext ".adoc") (easy-hugo-asciidoc-extension "adoc") (easy-hugo-server-flags "-D") :bind ("C-x M-h" . easy-hugo) ) ;; Automatically insert text in new files. (use-package autoinsert :after yasnippet :init (defun my/autoinsert-yas-expand() "Replace text in yasnippet template." (yas-minor-mode t) (yas-expand-snippet (buffer-string) (point-min) (point-max))) :config (add-to-list 'auto-insert-alist '(("\\.\\(cpp\\|cc\\|cxx\\|c\\+\\+\\)\\'" . "C++ program") . ["cpp" my/autoinsert-yas-expand])) (add-to-list 'auto-insert-alist '(("\\.\\(hpp\\|hh\\|hxx\\|h\\+\\+\\)\\'" . "C++ header") . ["hpp" my/autoinsert-yas-expand])) (add-to-list 'auto-insert-alist '(("\\.[1-9]\\.adoc\\'" . "AsciiDoc manpage") . ["manpage.adoc" my/autoinsert-yas-expand])) (add-to-list 'auto-insert-alist '(("\\.user.js\\'" . "Userscript") . ["user.js" my/autoinsert-yas-expand])) :custom (auto-insert-directory (concat user-emacs-directory "auto-insert")) (auto-insert-query nil) ; Don't ask before inserting. :hook (find-file . auto-insert)) ;;;;;;;;;;;;;;;;;;;; LaTeX ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (if (executable-find "xetex") (use-package tex-site :ensure auctex :custom (TeX-parse-self t) ; Enable parse on load. (TeX-auto-save t) ; Enable parse on save (TeX-PDF-mode t) ; PDF mode (rather than DVI-mode) (TeX-engine 'xetex) ;; To use pdf-tools with auctex. ;; https://emacs.stackexchange.com/a/21764/21935 (TeX-view-program-selection '((output-pdf "PDF Tools"))) (TeX-view-program-list '(("PDF Tools" TeX-pdf-tools-sync-view))) :mode ("\\.tex\\'" . LaTeX-mode) :config ;; To have the buffer refresh after compilation. (add-hook 'TeX-after-compilation-finished-functions #'TeX-revert-document-buffer))) ;; Auto complete for LaTeX. (use-package company-auctex :after auctex :hook (LaTeX-mode . company-auctex-init)) ;; Read RFC documents. (use-package rfc-mode :custom (rfc-mode-directory "/var/rfc/") ) ;;;;;;;;;;;;;;;;;;;; (X)HTML / CSS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Use company auto-completion for (X)HTML. (use-package company-web :after company :config (add-to-list 'company-backends 'company-web-html)) (use-package web-mode :mode ("\\.p?html?\\'" . web-mode) ("\\.tmpl\\'" . web-mode) ; Gitea templates ("\\.php\\'" . web-mode) :hook (web-mode . my/set-fill-column-100)) ;;;;;;;;;;;;;;;;;;;; Other file formats ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (use-package conf-mode :mode ("\\`/etc/conf\\.d/" . conf-mode) ; openrc config files. ("\\`/etc/portage/package\\.use/" . conf-space-mode) ; Portage config. ("\\`/etc/portage/package\\.accept_keywords/" . conf-space-mode) ("\\.pc\\(\\.in\\)?$" . conf-mode) ; pkg-config files. ("conanfile\\.txt$" . conf-mode) ; Conan recipes. ) (use-package adoc-mode :mode ("\\.adoc\\'" . adoc-mode) :custom-face ;; Style headers. (markup-title-0-face ((t (:inherit markup-gen-face :height 1.4 :weight bold)))) (markup-title-1-face ((t (:inherit markup-gen-face :height 1.3 :weight bold)))) (markup-title-2-face ((t (:inherit markup-gen-face :height 1.2 :weight bold)))) (markup-title-3-face ((t (:inherit markup-gen-face :height 1.1 :weight bold)))) (markup-title-4-face ((t (:inherit markup-gen-face :height 1.0 :weight bold :underline t)))) (markup-title-5-face ((t (:inherit markup-gen-face :height 1.0 :weight bold)))) ;; Enlarge meta-data to the same size as the other text. (markup-meta-face ((t (:inherit font-lock-comment-face)))) (markup-secondary-text-face ((t (:inherit markup-gen-face :height 1.0 :foreground "firebrick")))) (markup-hide-delimiter-face ((t (:inherit markup-meta-face)))) (markup-meta-hide-face ((t (:inherit markup-meta-face)))) ;; Style code snippets (``) (markup-verbatim-face ((t (:background "gray5")))) :hook (adoc-mode . auto-fill-mode) ; Wrap at fill-column. (adoc-mode . (lambda () ; Automatically update date. (setq-local time-stamp-pattern "8/:[dD[aA][tT][eE]: +%:y-%02m-%02d\n") ;; (setq-local time-stamp-time-zone nil) ; Set to local TZ. )) ) (use-package markdown-mode :custom (markdown-command "cmark") :custom-face ;; Style headers. (markdown-header-face-1 ((t (:inherit markdown-header-face :height 1.4 :weight bold)))) (markdown-header-face-2 ((t (:inherit markdown-header-face :height 1.3 :weight bold)))) (markdown-header-face-3 ((t (:inherit markdown-header-face :height 1.2 :weight bold)))) (markdown-header-face-4 ((t (:inherit markdown-header-face :height 1.1 :weight bold)))) (markdown-header-face-5 ((t (:inherit markdown-header-face :height 1.0 :weight bold :underline t)))) (markdown-header-face-6 ((t (:inherit markdown-header-face :height 1.0 :weight bold)))) (markdown-header-face-7 ((t (:inherit markdown-header-face :height 1.0 :weight bold)))) :mode (("README\\.md\\'" . gfm-mode) ("\\.md\\'" . markdown-mode) ("\\.markdown\\'" . markdown-mode)) :hook (markdown-mode . auto-fill-mode) ; Wrap at fill-column. ) (use-package crontab-mode :custom-face (outline-1 ((t (:inherit outline-1 ; Shrink minutes to normal size. :height 1.0)))) :mode ("/cron\\.d/" . crontab-mode) ("\\`'/etc/crontab\\'" . crontab-mode) :hook (crontab-mode . (lambda () (auto-fill-mode -1))) ) (use-package nginx-mode :defer t) (use-package company-nginx :after nginx-mode :hook (nginx-mode . company-nginx-keywords)) ;; View PDF files. (use-package pdf-tools :custom (pdf-misc-print-programm "/usr/bin/lpr") :mode ("\\.pdf\\'" . pdf-tools-install)) (use-package yaml-mode :mode ("\\.yml\\'" . yaml-mode) ) (use-package mediawiki) (use-package csv-mode :mode ("\\.[Cc][Ss][Vv]\\'" . csv-mode) ) ;; Read EPUB ebooks. (use-package nov :custom (nov-text-width fill-column) :custom-face (variable-pitch ((t (:inherit variable-pitch :family "Liberation Serif" :height 1.4)))) :mode ("\\.epub\\'" . nov-mode) ) ;;;;;;;;;;;;;;;;;;;; Server / Remote editing ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Edit remote files. (unless slow-computer (use-package tramp :custom (tramp-use-ssh-controlmaster-options nil) ; Don't override SSH config. (tramp-default-method "ssh") ; ssh is faster than scp and supports ports. (tramp-password-prompt-regexp ; Add verification code support. (concat "^.*" (regexp-opt '("passphrase" "Passphrase" "password" "Password" "Verification code") t) ".*:\0? *")) :config ;; Respect remote PATH. (add-to-list 'tramp-remote-path 'tramp-own-remote-path))) ;; Run server if: ;; - Our EUID is not 0, ;; - We are not logged in via SSH, ;; - It is not already running. (unless (equal (user-real-uid) 0) (unless (getenv "SSH_CONNECTION") (use-package server :defer 5 :init (setq server-use-tcp t server-port 51313 server-auth-key ; 64 chars, saved in ~/.emacs.d/server/server. "phahw2ohVoh0oopheish7IVie9desh8aequeenei3uo8wahShe%thuadaeNa4ieh") :config (unless (eq (server-running-p) t) ; Run server if not t. (server-start))) ;; Server for Firefox-extension that allows to use Emacs to edit textareas. ;; https://addons.mozilla.org/de/firefox/addon/edit-with-emacs1/ (use-package edit-server :config (edit-server-start) :custom (edit-server-url-major-mode-alist '( ("\\`likeable\\.space/" . markdown-mode) ("\\`schlomp\\.space/" . markdown-mode) ("\\`wiki\\.gentoo\\.org/" . mediawiki-mode) ("\\`github\\.com/" . markdown-mode) ("\\`gitlab\\.com/" . markdown-mode) ))) )) ;; Set garbage collection threshold to original value. (setq gc-cons-threshold (car (get 'gc-cons-threshold 'standard-value))) (provide 'init) ;;; init.el ends here