about summary refs log tree commit diff
path: root/users/sterni/emacs/init.el
diff options
context:
space:
mode:
Diffstat (limited to 'users/sterni/emacs/init.el')
-rw-r--r--users/sterni/emacs/init.el231
1 files changed, 202 insertions, 29 deletions
diff --git a/users/sterni/emacs/init.el b/users/sterni/emacs/init.el
index 4b868cb242..4cb741f62d 100644
--- a/users/sterni/emacs/init.el
+++ b/users/sterni/emacs/init.el
@@ -1,5 +1,9 @@
+;; set up package infrastructure
+
+(require 'use-package)
+(package-initialize)
+
 ;; Set default font and fallback font via set-fontset-font
-;; TODO(sterni): Investigate non-emoji representation of some glyphs
 (let ((mono-font "Bitstream Vera Sans Mono-12")
       (emoji-font "Noto Color Emoji-12"))
   (setq default-frame-alist `((font . ,mono-font)))
@@ -14,8 +18,9 @@
 (global-auto-revert-mode 1)
 
 ;; Indent
-(set-default 'indent-tabs-mode nil)
-(setq tab-width 2)
+(setq-default indent-tabs-mode nil)
+(setq tab-width 2
+      css-indent-offset tab-width)
 
 ;; UTF-8
 (setq locale-coding-system 'utf-8)
@@ -44,16 +49,37 @@
   (mouse-wheel-mode t)
   (blink-cursor-mode -1))
 
-;; TODO(sterni): prevent some remaining backup files
+;; /tmp is a tmpfs, but we may want to recover from power loss
+(custom-set-variables
+ `(temporary-file-directory ,(concat (getenv "HOME") "/.emacs/tmp")))
+
 (setq auto-save-file-name-transforms
       `((".*" ,temporary-file-directory t)))
 (setq backup-directory-alist
       `((".*" . ,temporary-file-directory)))
+(setq undo-tree-history-directory-alist
+      `((".*" . ,temporary-file-directory)))
+(setq backup-by-copying t)
+(setq create-lockfiles nil)
+
+;; save history
+(savehist-mode)
+(setq savehist-additional-variables '(search-ring regexp-search-ring magit-cl-history))
 
 ;; buffers
+
+;; performance migitations
+(global-so-long-mode)
+
 ;; unique component should come first for better completion
 (setq uniquify-buffer-name-style 'forward)
 
+;; completions
+(ido-mode 1)
+(setq ido-enable-flex-matching t)
+(ido-everywhere)
+(fido-mode)
+
 ;; Display column numbers
 (column-number-mode t)
 (setq-default fill-column 80)
@@ -64,6 +90,9 @@
 (setq whitespace-style '(face trailing tabs)
       whitespace-line-column fill-column)
 (add-hook 'prog-mode-hook #'whitespace-mode)
+(setq-default indicate-empty-lines t)
+(setq-default indicate-buffer-boundaries 'left)
+(setq sentence-end-double-space nil)
 
 ;;; Configure built in modes
 
@@ -72,14 +101,63 @@
 (setq perl-continued-statement-offset 0)
 (setq perl-continued-brace-offset 0)
 
-;;; Configure packages
-(require 'use-package)
+;; org mode
 
-(package-initialize)
+(setq org-clock-persist 'history)
+(org-clock-persistence-insinuate)
+
+(let ((org-folder (concat (getenv "HOME") "/files/sync/org")))
+  (setq org-agenda-files (directory-files-recursively org-folder "\\.org$")
+        org-default-notes-file (concat org-folder "/inbox.org")
+        initial-buffer-choice org-default-notes-file
+        org-refile-targets '((org-agenda-files . (:maxlevel . 2)))))
+
+;; latex
+
+(defun latex-word-count ()
+  "Calls texcount on the file the current buffer points to and displays the result."
+  (interactive)
+  (save-buffer)
+  (let* ((file (buffer-file-name)) ; needs to happen outside with-current-buffer
+         (word-count
+             (with-output-to-string
+               (with-current-buffer standard-output
+                 (call-process "texcount" nil t nil "-brief" "-utf8" file)))))
+      (message (string-trim-right word-count))))
+
+;; ediff
+;; doesn't create new window for ediff controls which I always open accidentally
+(setq ediff-window-setup-function 'ediff-setup-windows-plain)
+
+;; man
+(setq Man-notify-method 'pushy) ; display man page in current window
+
+;; shell
 
+; default, but allows ';' as prompt
+(setq shell-prompt-pattern "^[^#$%>;\n]*[#$%>;] *")
+
+;; projects (see also evil config)
+
+(defun project-magit ()
+  "Run magit in the current project dir"
+  (interactive)
+  (magit (project-root (project-current t))))
+
+(define-key project-prefix-map (kbd "G") 'project-magit)
+
+(setq project-switch-commands
+      '((project-find-file "Find file")
+        (project-find-regexp "Find regexp")
+        (project-dired "Dired")
+        (project-shell "Shell")
+        (project-magit "Magit")))
+
+;;; Configure packages
 (use-package undo-tree
   :config
-  (global-undo-tree-mode))
+  (global-undo-tree-mode)
+  (setq undo-tree-auto-save-history t))
 
 (use-package magit
   :after evil
@@ -88,7 +166,9 @@
   ; gerrit doesn't like 80 column commit messages…
   (add-hook 'git-commit-mode-hook (lambda () (setq fill-column 72)))
   (evil-define-key 'normal 'global (kbd "<leader>gr") 'magit-status))
-(use-package tvl :after magit)
+(use-package tvl
+  :after magit
+  :custom tvl-depot-path (concat (getenv "HOME") "/src/depot"))
 
 (setq ediff-split-window-function 'split-window-horizontally)
 
@@ -107,45 +187,107 @@
   ;; buffer management
   (evil-define-key 'normal 'global (kbd "<leader>bk") 'kill-buffer)
   (evil-define-key 'normal 'global (kbd "<leader>bb") 'switch-to-buffer)
-  ;; window management
-  (evil-define-key 'normal 'global (kbd "<leader>wk") 'delete-window)
-  (evil-define-key 'normal 'global (kbd "<leader>wo") 'delete-other-window)
-  (evil-define-key 'normal 'global (kbd "<leader>wh") 'split-window-below)
-  (evil-define-key 'normal 'global (kbd "<leader>wv") 'split-window-right)
-  (evil-define-key 'normal 'global (kbd "<leader>ww") 'other-window)
+  (evil-define-key 'normal 'global (kbd "<leader>bo") 'switch-to-buffer-other-window)
+  (evil-define-key 'normal 'global (kbd "<leader>bl") 'list-buffers)
+  (evil-define-key 'normal 'global (kbd "<leader>br") 'revert-buffer)
+  ;; window management: C-w hjkl is annoying in neo
+  (define-key evil-window-map (kbd "<left>") 'evil-window-left)
+  (define-key evil-window-map (kbd "<right>") 'evil-window-right)
+  (define-key evil-window-map (kbd "<up>") 'evil-window-up)
+  (define-key evil-window-map (kbd "<down>") 'evil-window-down)
+  ;; projects
+  (evil-define-key 'normal 'global (kbd "<leader>pf") 'project-find-file)
+  (evil-define-key 'normal 'global (kbd "<leader>pg") 'project-find-regexp)
+  (evil-define-key 'normal 'global (kbd "<leader>pd") 'project-dired)
+  (evil-define-key 'normal 'global (kbd "<leader>ps") 'project-shell)
+  (evil-define-key 'normal 'global (kbd "<leader>pR") 'project-query-replace-regexp)
+  (evil-define-key 'normal 'global (kbd "<leader>pK") 'project-kill-buffers)
+  (evil-define-key 'normal 'global (kbd "<leader>pp") 'project-switch-project)
   ;; emacs
   (evil-define-key 'visual 'global (kbd "<leader>ee") 'eval-region)
   (evil-define-key 'normal 'global (kbd "<leader>ee") 'eval-last-sexp)
   (evil-define-key 'normal 'global (kbd "<leader>ep") 'eval-print-last-sexp)
   (evil-define-key 'normal 'global (kbd "<leader>eh") 'help)
+  (evil-define-key 'normal 'global (kbd "<leader>em") 'man)
+  (evil-define-key '(normal visual) 'global (kbd "<leader>eu") 'browse-url-at-point)
+  (evil-define-key '(normal visual) 'global (kbd "<leader>ef") 'ffap)
   ;; modify what is displayed
   (evil-define-key 'normal 'global (kbd "<leader>dw")
     (lambda ()
       (interactive)
       (whitespace-mode 'toggle)
       (display-fill-column-indicator-mode 'toggle)))
-  ;; elfeed bindings for evil (can't use-package elfeed apparently)
-  (evil-define-key 'normal 'global (kbd "<leader>ff") 'elfeed)
-  (evil-define-key '(normal visual) elfeed-search-mode-map
-    (kbd "o") 'elfeed-search-browse-url
-    (kbd "r") 'elfeed-search-untag-all-unread
-    (kbd "u") 'elfeed-search-tag-all-unread
-    (kbd "<leader>ff") 'elfeed-search-fetch
-    (kbd "<leader>fc") 'elfeed-db-compact
-    (kbd "<leader>fr") 'elfeed-search-update--force))
+  ;; org-mode
+  (evil-define-key 'normal 'global (kbd "<leader>oa") 'org-agenda)
+  (evil-define-key 'normal 'global (kbd "<leader>oc") 'org-capture))
 
 (use-package evil-collection
   :after evil
   :config
   (evil-collection-init))
 
+;; parens
 (use-package rainbow-delimiters
-  :hook (prog-mode . rainbow-delimiters-mode))
+  :hook ((prog-mode . rainbow-delimiters-mode)))
+
+(setq show-paren-delay 0)
+(show-paren-mode)
+
+(use-package paredit
+  :hook ((emacs-lisp-mode . paredit-mode)
+         (lisp-mode . paredit-mode)
+         (ielm-mode . paredit-mode)
+         (lisp-interaction-mode . paredit-mode)))
+
+(use-package which-key :config (which-key-mode t))
 
 (use-package nix-mode :mode "\\.nix\\'")
 (use-package nix-drv-mode :mode "\\.drv\\'")
 
+(use-package direnv
+  :config (direnv-mode))
+
+(use-package editorconfig
+  :config (editorconfig-mode 1))
+
 (use-package haskell-mode)
+(use-package flycheck
+  :init (global-flycheck-mode)
+  :custom flycheck-keymap-prefix (kbd "<leader>!"))
+(use-package lsp-mode
+  :hook ((haskell-mode . lsp-deferred))
+  :commands (lsp lsp-deferred)
+  :custom
+  lsp-modeline-code-actions-segments '() ; using lsp-ui-sideline instead
+  :config
+  (evil-define-key 'normal 'global
+    (kbd "<leader>lwr") 'lsp-workspace-restart
+    (kbd "<leader>lwq") 'lsp-workspace-shutdown
+    (kbd "<leader>la=") 'lsp-format-buffer
+    (kbd "<leader>lar") 'lsp-rename
+    (kbd "<leader>laa") 'lsp-execute-code-action))
+(use-package lsp-ui
+  :after lsp-mode
+  :custom
+  lsp-ui-doc-enable t
+  lsp-ui-doc-border "DimGray"
+  lsp-ui-doc-delay 0.5
+  :config
+  (set-face-background 'lsp-ui-doc-background "WhiteSmoke")
+  (set-face-foreground 'lsp-ui-sideline-code-action "SaddleBrown")
+  (setq lsp-ui-sideline-code-actions-prefix "🔨 "
+        lsp-ui-sideline-show-diagnostics nil
+        lsp-ui-sideline-show-code-actions t) ; is :custom, but won't take effect?
+  (evil-define-key 'normal lsp-ui-mode-map
+    ;; TODO(sterni): emulate using xref for non-lsp?
+    (kbd "<leader>lgr") 'lsp-ui-peek-find-references
+    (kbd "<leader>lgd") 'lsp-ui-peek-find-definitions
+    (kbd "<leader>lc") 'lsp-ui-flycheck-list))
+(use-package lsp-haskell
+  :after lsp-mode
+  :custom
+  lsp-haskell-formatting-provider "ormolu")
+
 (use-package urweb-mode)
 (use-package bqn-mode
   :mode "\\.bqn\\'"
@@ -157,11 +299,14 @@
 (use-package rust-mode)
 (use-package sly
   :after evil
-  :hook ((sly-mrepl-mode . (lambda () (rainbow-delimiters-mode-enable))))
+  :hook ((sly-mrepl-mode . (lambda ()
+                             (enable-paredit-mode)
+                             (rainbow-delimiters-mode-enable))))
   :config
-  (evil-define-key 'normal sly-mrepl-mode-map (kbd "C-r") 'isearch-backward))
+  (evil-define-key '(normal insert) sly-mrepl-mode-map (kbd "C-r") 'isearch-backward))
 
-(use-package ada-mode)
+; TODO(sterni): https://github.com/NixOS/nixpkgs/pull/173893/files
+; (use-package ada-mode)
 
 (use-package rainbow-mode)
 (use-package hl-todo
@@ -178,7 +323,35 @@
   :mode (("\\.md\\'" . markdown-mode)))
 (use-package adoc-mode
   :mode (("\\.adoc\\'" . adoc-mode)))
+(use-package languagetool
+  :after evil
+  :custom
+  languagetool-java-arguments '("-Dfile.encoding=UTF-8")
+  languagetool-default-language "en-GB"
+  languagetool-mother-tongue "de-DE"
+  :config
+  (evil-define-key 'normal 'global (kbd "<leader>mll") 'languagetool-check)
+  (evil-define-key 'normal 'global (kbd "<leader>mlc") 'languagetool-correct-at-point)
+  (evil-define-key 'normal 'global (kbd "<leader>mls") 'languagetool-set-language)
+  (evil-define-key 'normal 'global (kbd "<leader>mlr") 'languagetool-clear-suggestions)
+  ;; Fill background of issues instead of just underlining to make it easier to read
+  (set-face-background 'languagetool-issue-default "yellow")
+  (set-face-background 'languagetool-issue-misspelling "red"))
+
+(use-package deft
+  :config
+  ;; This is based on (car deft-extensions), but unfortunately the variable is
+  ;; not re-bound in the hook defined by defcustom, so it is always "txt".
+  (setq deft-default-extension "org")
+  (evil-define-key 'normal 'global (kbd "<leader>mn") 'deft)
+  :custom
+  deft-directory (expand-file-name "~/files/sync/org/notes")
+  deft-extensions '("org" "md" "txt" "tex"))
+
+(unless (server-running-p)
+  (server-start))
 
-(require 'subscriptions)
+(require 'subscriptions) ; elfeed config
+(require 'nix-inject)
 
 (provide 'init)