Skip to content

Latest commit

 

History

History
984 lines (787 loc) · 28.8 KB

emacs.org

File metadata and controls

984 lines (787 loc) · 28.8 KB

Emacs Configuration

Table of Contents

Basic Setup

Load the utils file. It contains a collection of elisp functions collected from the internet.

(use-package zach-utils
  :load-path "lisp/")

Make sure the theme is available before loading the customize file.

(use-package dracula-theme
  :ensure t
  :demand
  :config (load-theme 'dracula t))

Move the customize config to it’s own file.

(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
(load custom-file)

Create a group for config settings which can be modifed in customize.

(defgroup my-config nil
  "Configuration settings"
  :group 'local)

Suppress warnings for functions redefined by defadvice.

(setq ad-redefinition-action 'accept)

UI

No need to have the startup message, or tool & scroll bars.

(setq inhibit-startup-message t)
(tool-bar-mode -1)
(scroll-bar-mode -1)

Only show the menu bar on OSX.

(when (not (eq system-type `darwin))
  (menu-bar-mode -1))

Set the default window size.

(when window-system
  (set-frame-size (selected-frame) 150 40))

Show git diffs in the left fringe.

(use-package git-gutter-fringe
  :ensure t
  :defer 5
  :diminish git-gutter-mode
  :config (global-git-gutter-mode))

Cleanup the modeline. This should be done after the custom-file is loaded or you will get the safe theme prompt on each startup.

(use-package smart-mode-line
  :ensure t
  :init (setq sml/theme 'respectful
              sml/shorten-directory t
              sml/shorten-modes t)
  :config (sml/setup))

Make the scrolling smoother by going a line at a time, for both the keyboard and mouse

(setq scroll-margin 1
      scroll-step 1
      scroll-conservatively 10000
      scroll-preserve-screen-position 1

      mouse-wheel-follow-mouse 't
      mouse-wheel-scroll-amount '(1 ((shift) .1)))

Customize the scratch buffer.

(setq initial-scratch-message
      ";; scratch buffer -- hack away!")

Font

Set the font based on OS.

(pcase system-type
  (`darwin     (set-frame-font "Iosevka-13:weight=regular"))
  (`gnu/linux  (set-frame-font "Meslo LG M 10"))
  (`windows-nt (set-frame-font "Cascadia Code 9")))

(setq-default line-spacing 3)

Use 4 spaces for indentation.

(setq-default tab-width 4)

Use font-lock everywhere.

(global-font-lock-mode 1)

Misc

Some OSX specific settings.

(use-package exec-path-from-shell
  :ensure t
  :if (eq system-type 'darwin)
  :init
  (setq exec-path-from-shell-check-startup-files nil
        frame-resize-pixelwise t ;; Prevent's gaps using --with-no-titlebar
        mac-option-modifier 'super
        mac-command-modifier 'meta) ;; Use cmd as meta.
  :config
  (exec-path-from-shell-initialize)
  ;; Make ansi-term play nice with zsh prompt.
  (defadvice ansi-term (after advise-ansi-term-coding-system)
    (set-buffer-process-coding-system 'utf-8-unix 'utf-8-unix)))

Prevent Magit from showing it’s instructions buffer on each startup.

(use-package magit
  :ensure t
  :bind ("C-x g" . magit-status)
  :init
  ;; Prevent Magit from showing it's instructions buffer on each startup.
  (setq magit-last-seen-setup-instructions "1.4.0")
  :config
  (use-package magit-popup
    :ensure t))

Here we define some global key bindings.

(fill-keymap 'global
             "C-x C-b" 'ibuffer
             "C-a"     'smart-line-beginning
             "<f11>"   'toggle-fullscreen
             "C-c e"   'erc-start-or-switch
             "C-c t"   'visit-term-buffer
             "C-j"     'newline-and-indent)

General package configuration

(use-package whitespace
  :init (setq whitespace-style '(face empty lines-tail trailing)
              whitespace-line-column 100))

(column-number-mode 1)

(setq-default indent-tabs-mode nil)

Enable the EditorConfig plugin.

(use-package editorconfig
  :ensure t
  :defer 2
  :config (editorconfig-mode))

Use paradox as a replacement to the built-in package menu.

(use-package paradox
  :ensure t
  :defer 10)

Projectile

(use-package projectile
  :ensure t
  :defer 2
  :config
  (projectile-mode)
  (use-package helm-projectile
    :ensure t
    :bind ("C-c p h")
    :config (helm-projectile-on)))

Helm

(use-package helm-company
  :ensure t
  :bind (:map company-mode-map
              ("C-:" . helm-company)
              :map company-active-map
              ("C-:" . helm-company)))

(use-package helm-flx
  :ensure t)

(use-package helm-flycheck
  :ensure t
  :bind (:map flycheck-mode-map
              ("C-c ! h" . helm-flycheck)))

(use-package helm
  :ensure t
  :defer 1
  :diminish helm-mode
  :bind (("M-x"       . helm-M-x)
         ("M-y"       . helm-show-kill-ring)
         ("C-x C-f"   . helm-find-files)
         ("C-c <SPC>" . helm-all-mark-rings)
         ("C-x r b"   . helm-filtered-bookmarks)
         ("C-h r"     . helm-info-emacs)
         ("C-,"       . helm-calcul-expression)
         ("C-h i"     . helm-info-at-point)
         ("C-x C-d"   . helm-browse-project)
         ("C-h C-f"   . helm-apropos)
         ("C-h a"     . helm-apropos)
         ("C-s"       . helm-occur))
  :init (setq helm-M-x-fuzzy-match t)
  :config
  (require 'helm-config)
  (helm-mode)
  (helm-autoresize-mode)
  (helm-flx-mode))

Uniquify

(setq uniquify-buffer-name-style 'post-forward)

Paren

(show-paren-mode t)
(setq show-paren-delay 0
      show-paren-style 'parenthesis)

Auto Complete

(use-package auto-complete
  :ensure t
  :commands auto-complete-mode
  :init
  (setq ac-auto-start 0
        ac-quick-help-delay 0.5
        ac-fuzzy-enable t
        ac-use-fuzzy t
        ac-auto-show-menu 0.2)
  :config
  (add-to-list 'ac-dictionary-directories (concat user-emacs-directory "ac-dict"))
  (ac-config-default))
(use-package company
  :ensure t
  :commands company-mode
  :init
  ;; Set company-mode to try and complete instantly
  (setq company-idle-delay 0)
  :config
  (use-package company-irony
    :ensure t
    :init (add-to-list 'company-backends 'company-irony))
  (use-package company-c-headers
    :ensure t
    :init (add-to-list 'company-backends 'company-c-headers)))

Org

Enable spell checking and syntax highlighting in org-mode.

(use-package org
  :init (setq org-src-fontify-natively t)
  :config
  (flyspell-mode)
  (require 'org-tempo))

Enable toc-org so this file can have a ToC in Github without any exporting.

(use-package toc-org
  :ensure t
  :commands toc-org-enable
  :init (add-hook 'org-mode-hook 'toc-org-enable))

Enable journal mode, always in indented mode. The journal dir is set based on OS, and all journals added to the org-agenda-files list.

(use-package org-journal
  :ensure t
  :config
  (setq org-startup-indented t)

  (let ((j-dir (cond ((eq system-type 'darwin) "~/Dropbox/org-journals")
                     ((eq system-type 'windows-nt) "~/../../Dropbox/org-journals/"))))
    (setq org-journal-dir j-dir)
    (add-to-list 'org-agenda-files j-dir)))

Paredit

(use-package paredit
  :ensure t
  :bind (:map paredit-mode-map
              ("M-s" . paredit-splice-sexp)
              ("M-S" . paredit-split-sexp)
              ("M-j" . paredit-join-sexps)

              ("M-o" . paredit-forward-down)
              ("M-O" . paredit-forward-up)
              ("M-u" . paredit-backward-down)
              ("M-U" . paredit-backward-up)

              ("M-l" . paredit-forward)
              ("M-h" . paredit-backward)
              ("M-k" . paredit-kill)
              ("M-(" . backward-barf-sexp)
              ("M-)" . forward-barf-sexp)
              ("C-(" . backward-slurp-sexp)
              ("C-)" . forward-slurp-sexp))
  :config
  ;; Some enhanced fns I found somewhere.
  (defun forward-barf-sexp (prefix)
    "Calls `paredit-forward-barf-sexp', unless PREFIX is non nil.
               With prefix it calls `paredit-barf-all-the-way-forward'"
    (interactive "P")
    (if prefix
        (paredit-barf-all-the-way-forward)
      (paredit-forward-barf-sexp)))

  (defun forward-slurp-sexp (prefix)
    "Calls `paredit-forward-slurp-sexp`, unless PREFIX is non nil.
               With prefix it calls `paredit-slurp-all-the-way-forward'"
    (interactive "P")
    (if prefix
        (paredit-slurp-all-the-way-forward)
      (paredit-forward-slurp-sexp)))

  (defun backward-barf-sexp (prefix)
    "Calls `paredit-backward-barf-sexp', unless PREFIX is non nil.
               With prefix it calls `paredit-barf-all-the-way-backward'"
    (interactive "P")
    (if prefix
        (paredit-barf-all-the-way-backward)
      (paredit-backward-barf-sexp)))

  (defun backward-slurp-sexp (prefix)
    "Calls `paredit-backward-slurp-sexp', unless PREFIX is non nil.
               With prefix it calls `paredit-slurp-all-the-way-backward'"
    (interactive "P")
    (if prefix
        (paredit-slurp-all-the-way-backward)
      (paredit-backward-slurp-sexp)))

  ;; Enable Paredit in the minibuffer.
  (defvar paredit-minibuffer-commands '(eval-expression
                                        pp-eval-expression
                                        eval-expression-with-eldoc
                                        ibuffer-do-eval
                                        ibuffer-do-view-and-eval)
    "Interactive commands for which paredit should be enabled in the minibuffer.")

  (add-hook 'minibuffer-setup-hook
            (lambda ()
              (if (memq this-command paredit-minibuffer-commands)
                  (enable-paredit-mode)))))

IBuffer

Use Purcell’s ibuffer-vc for grouping.

(use-package ibuffer-vc
  :ensure t
  :defer 5
  :init
  ;; Include version control status info in the ibuffer list.
  (setq ibuffer-formats
        '((mark modified read-only vc-status-mini " "
                (name 18 18 :left :elide)
                " "
                (size 9 -1 :right)
                " "
                (mode 16 16 :left :elide)
                " "
                (vc-status 16 16 :left)
                " "
                filename-and-process)))
  :config (add-hook 'ibuffer-hook (lambda()
                                    (ibuffer-vc-set-filter-groups-by-vc-root)
                                    (unless (eq ibuffer-sorting-mode 'alphabetic)
                                      (ibuffer-do-sort-by-alphabetic)))))

Markdown

Install `markdown-mode`

(use-package markdown-mode
  :ensure t
  :commands (markdown-mode gfm-mode)
  :mode (("README\\.md\\'" . gfm-mode)
         ("\\.md\\'" . markdown-mode)
         ("\\.markdown\\'" . markdown-mode)))

NeoTree

(defun neotree-project-dir ()
  "Open NeoTree using the git root."
  (interactive)
  (let ((project-dir (projectile-project-root))
        (file-name   (buffer-file-name)))
    (neotree-toggle)
    (if project-dir
        (if (neo-global--window-exists-p)
            (progn
              (neotree-dir project-dir)
              (neotree-find file-name)))
      (message "Could not find git project root."))))

(use-package neotree
  :ensure t
  :commands neotree-toggle
  :bind ("M-\\" . neotree-project-dir)
  :config
  (use-package all-the-icons
    :ensure t)
  (setq neo-theme (if (display-graphic-p)
                      'icons
                    'arrow)))

Ledger

(use-package ledger-mode
  :ensure t
  :commands ledger-mode)

Backup

Some backup settings. These should be cleaned up in the future.

(setq auto-save-file-name-transforms '((".*" "~/.emacs.d/autosaves/\\1" t))
      backup-directory-alist '((".*" . "~/.emacs.d/backups/")))

(make-directory (concat user-emacs-directory "autosaves/") t)

(setq vc-make-backup-files t
      backup-by-copying t
      delete-old-versions t
      kept-new-versions 6
      kept-old-versions 2
      version-control t)

Convenience functions

Split the current buffer with a terminal.

(defun visit-term-buffer ()
  "Create or visit a terminal buffer."
  (interactive)
  (if (not (get-buffer "*ansi-term*"))
      (progn
        (split-window-sensibly (selected-window))
        (other-window 1)
        (ansi-term (getenv "SHELL")))
    (switch-to-buffer-other-window "*ansi-term*")))

Programming Packages

General Setup

Set some keywords to be highlighted.

(use-package fic-mode
  :ensure t
  :commands (fic-mode)
  :init (setq fic-highlighted-words '("FIXME" "FIXME:" "TODO" "TODO:"
                                      "HACK" "HACK:" "NOTE" "NOTE:"
                                      "BUG" "BUG:" "REFACTOR" "REFACTOR:")))

Enable some basic packages for every programming language.

(defcustom programming-language-major-modes
  '(prog-mode     ; This is the mode perl, makefile, lisp-mode, scheme-mode,
                  ; emacs-lisp-mode, sh-mode, java-mode, c-mode, c++-mode,
                  ; python-mode inherits from.
    lua-mode
    cmake-mode
    tex-mode                            ; LaTeX inherits
    sgml-mode                           ; HTML inherits
    css-mode
    nxml-mode
    diff-mode
    haskell-mode
    rst-mode
    arduino-mode)
  "What to consider as programming languages."
  :group 'my-config)

(use-package rainbow-delimiters
  :ensure t
  :commands (rainbow-delimiters-mode))

(use-package smartparens
  :ensure t
  :commands (smartparens-mode smartparens-strict-mode)
  :config (require 'smartparens-config))

(dolist (mode programming-language-major-modes)
  (add-hook
   (intern (concat (symbol-name mode) "-hook"))
   (lambda ()
     (fic-mode)
     (rainbow-delimiters-mode)
     (whitespace-mode)
     (smartparens-mode))))

Don’t ask to save before compilation.

(setq compilation-ask-about-save nil
      compilation-window-height 30)

Setup flycheck.

(use-package flycheck
  :ensure t
  :defer 5
  :commands (global-flycheck-mode flycheck-add-mode)

  :init
  (global-flycheck-mode)
  (setq flycheck-completion-system 'ido
        flycheck-disabled-checkers '(emacs-lisp-checkdoc))

  ;; Remove newline checks, since they would trigger an immediate check
  ;; when we want the idle-change-delay to be in effect while editing.
  (setq flycheck-check-syntax-automatically '(save
                                              idle-change
                                              mode-enabled))


  :config
  (defun magnars/adjust-flycheck-automatic-syntax-eagerness ()
    "Adjust how often we check for errors based on if there are any.

This lets us fix any errors as quickly as possible, but in a clean buffer we're an order
of magnitude laxer about checking."
    (setq flycheck-idle-change-delay
          (if flycheck-current-errors 0.5 5.0)))

  (add-hook 'flycheck-after-syntax-check-hook
            'magnars/adjust-flycheck-automatic-syntax-eagerness)

  ;; Each buffer gets its own idle-change-delay because of the
  ;; buffer-sensitive adjustment above.
  (make-variable-buffer-local 'flycheck-idle-change-delay)

  (defun flycheck-handle-idle-change ()
    "Handle an expired idle time since the last change.

This is an overwritten version of the original flycheck-handle-idle-change, which removes
the forced deferred. Timers should only trigger inbetween commands in a single threaded
system and the forced deferred makes errors never show up before you execute another command."
    (flycheck-clear-idle-change-timer)
    (flycheck-buffer-automatically 'idle-change))

  (use-package flycheck-package
    :ensure t
    :config (flycheck-package-setup)))

Setup the indentation style for C-based languages (C, C++, Java, etc).

(setq c-default-style "stroustrup")

Support for `*.toml` files.

(use-package toml-mode
  :ensure t
  :defer 10
  :commands toml-mode)

Emacs Lisp

Enable a few packages for elisp-related modes.

(defcustom elisp-programming-major-modes
  '(emacs-lisp-mode
    lisp-interaction-mode
    ielm-mode)
  "Modes that are used to do Elisp programming.")

(defun my-elisp-setup ()
  "Enable some packages for any elisp related buffer."
  (eldoc-mode)
  (paredit-mode)
  (rainbow-delimiters-mode)
  (smartparens-mode -1)
  (auto-complete-mode -1)
  (company-mode))

(dolist (mode elisp-programming-major-modes)
  (add-hook (intern (concat (symbol-name mode) "-hook"))
            'my-elisp-setup))

C#

(use-package csharp-mode
  :ensure t
  :defer t
  :config
  (use-package omnisharp
    :ensure t
    :init (add-hook 'csharp-mode-hook 'omnisharp-mode)))

Clojure

(use-package clojure-mode
  :ensure t
  :commands (clojure-mode clojurescript-mode)
  :config
  ;; On OSX, each Java process started by Cider or Leiningen will show up on
  ;; the dock. The following option prevents that.
  (setenv "LEIN_JVM_OPTS" "-Dapple.awt.UIElement=true")


  (add-hook 'clojure-mode-hook
            (lambda ()
              ;; Disable smartparens in favor of paredit
              (smartparens-mode -1)
              (paredit-mode)

              ;; Make sure Eldoc and Company are running
              (auto-complete-mode -1)
              (company-mode)
              (eldoc-mode)))

  ;; Set up some custom identation rules
  (define-clojure-indent
    (defroutes 'defun)
    (GET 2)
    (POST 2)
    (PUT 2)
    (DELETE 2)
    (HEAD 2)
    (ANY 2)
    (context 2))

  ;; Use symbols for 'fn', function literals, and sets.
  (font-lock-add-keywords
   'clojure-mode `(("(\\(fn\\)[\[[:space:]]"
                    (0 (progn (compose-region (match-beginning 1)
                                              (match-end 1) "λ")
                              nil)))))
  (font-lock-add-keywords
   'clojure-mode `(("\\(#\\)("
                    (0 (progn (compose-region (match-beginning 1)
                                              (match-end 1) "ƒ")
                              nil)))))
  (font-lock-add-keywords
   'clojure-mode `(("\\(#\\){"
                    (0 (progn (compose-region (match-beginning 1)
                                              (match-end 1) "")
                              nil))))))

(use-package cider
    :ensure t
    :commands (cider-mode cider-repl-mode)
    :init
    (setq nrepl-hide-special-buffers t
          cider-auto-select-error-buffer nil
          cider-repl-result-prefix ";; => "
          cider-repl-use-pretty-printing t
          cider-repl-use-clojure-font-lock t)
    (add-hook 'cider-mode-hook (lambda ()
                                 (eldoc-mode)
                                 (company-mode)))
    (add-hook 'cider-repl-mode-hook (lambda ()
                                      (eldoc-mode)
                                      (company-mode)
                                      (paredit-mode)))
    :config

    ;; Save buffer before testing or loading.
    (defadvice clojure-test-run-tests (before save-first activate)
      (save-buffer))
    (defadvice cider-load-current-buffer (before save-first activate)
      (save-buffer)))

(use-package cljsbuild-mode
  :ensure t
  :commands (cljsbuild-start)
  :init
  ;; Use "lein trampoline" when building Clojurescript, and hide the buffer when the
  ;; build was successful.
  (setq cljsbuild-hide-buffer-on-success t
        cljsbuild-compile-command "lein trampoline cljsbuild auto"))

Javascript

Use web-mode and js2-mode.

(use-package web-mode
  :ensure t
  :mode "\\.jsx$"
  :commands web-mode
  :init
  (flycheck-add-mode 'javascript-eslint 'web-mode)
  (setq web-mode-markup-indent-offset 2
        web-mode-css-indent-offset 2
        web-mode-code-indent-offset 2
        web-mode-indent-style 2)
  :config
  (defadvice web-mode-highlight-part (around tweak-jsx-activate)
    (if (equal web-mode-content-type "jsx")
        (let ((web-mode-enable-part-face nil))
          ad-do-it)
      ad-do-it)))

(use-package js2-mode
  :ensure t
  :commands js2-mode
  :mode (("\\.js$" . js2-mode)
         ("\\.es6$" . js2-mode)
         ("\\.ejs$" . js2-mode))
  :interpreter "node"
  :init
  (setq js-indent-level 2))

Disable jshint in favor of eslint.

(setq-default flycheck-disabled-checkers
              (append flycheck-disabled-checkers
                      '(javascript-jshint)))

Enable NVM support.

(when (file-exists-p "~/.nvm")
  (use-package nvm
    :ensure t
    :defer 5
    :config
    (nvm-use (caar (last (nvm--installed-versions))))))

Check for a local eslint after switching Projectile projects.

(defun setup-local-eslint ()
  "If ESLint found in node_modules directory, use that for flycheck."
  (interactive)
  (let ((local-eslint (expand-file-name "./node_modules/.bin/eslint")))
    (setq flycheck-javascript-eslint-executable
          (and (file-exists-p local-eslint) local-eslint))))

(add-hook 'projectile-after-switch-project-hook 'setup-local-eslint)

Rust

Setup based on https://robert.kra.hn/posts/2021-02-07_rust-with-emacs/

Use Rustic, a fork of rust-mode.

(use-package rustic
  :ensure
  :bind (:map rustic-mode-map
              ("M-j" . lsp-ui-imenu)
              ("M-?" . lsp-find-references)
              ("C-c C-c l" . flycheck-list-errors)
              ("C-c C-c a" . lsp-execute-code-action)
              ("C-c C-c r" . lsp-rename)
              ("C-c C-c q" . lsp-workspace-restart)
              ("C-c C-c Q" . lsp-workspace-shutdown)
              ("C-c C-c s" . lsp-rust-analyzer-status))
  :config
  ;; uncomment for less flashiness
  ;; (setq lsp-eldoc-hook nil)
  ;; (setq lsp-enable-symbol-highlighting nil)
  ;; (setq lsp-signature-auto-activate nil)

  ;; comment to disable rustfmt on save
  (setq rustic-format-on-save t)
  (add-hook 'rustic-mode-hook 'rk/rustic-mode-hook))

(defun rk/rustic-mode-hook ()
  ;; so that run C-c C-c C-r works without having to confirm
  (setq-local buffer-save-without-query t))

Use lsp-mode and lsp-ui-mode.

(use-package lsp-mode
  :ensure
  :commands lsp
  :custom
  ;; what to use when checking on-save. "check" is default, I prefer clippy
  (lsp-rust-analyzer-cargo-watch-command "clippy")
  (lsp-eldoc-render-all t)
  (lsp-idle-delay 0.6)
  (lsp-rust-analyzer-server-display-inlay-hints t)
  :config
  (add-hook 'lsp-mode-hook 'lsp-ui-mode))

(use-package lsp-ui
  :ensure
  :commands lsp-ui-mode
  :custom
  (lsp-ui-peek-always-show t)
  (lsp-ui-sideline-show-hover t)
  (lsp-ui-doc-enable nil))