Permalink
Switch branches/tags
Nothing to show
Find file
fa2bd4d Oct 9, 2017
1595 lines (1311 sloc) 46.4 KB

Utility Functions

std::what-face

(defun std::what-face (point)
  "Reveal face at POINT."
  (interactive "d")
  (let ((face (or (get-char-property (point) 'read-face-name)
                  (get-char-property (point) 'face))))
    (if face (message "Face: %s" face) (message "No face at %d" point))))
(global-set-key (kbd "C-x ö") #'std::what-face)

std::what-major-mode

(defun std::what-major-mode ()
  "Reveal current major mode."
  (interactive)
  (message "%s" major-mode))
(global-set-key (kbd "C-x ä") #'std::what-major-mode)

std::edit-org-user-config

(defun std::edit-org-user-config ()
  "Edit the org user config file, in the current window."
  (interactive)
  (find-file-existing (concat (getenv "SPACEMACSDIR") "/user-config.org")))

(evil-leader/set-key
  "feo" #'std::edit-org-user-config)

std::def-key-for-maps

(defun std::def-key-for-maps (key cmd keymaps)
  "Bind KEY to CMD for all keymaps in MAPS."
  (dolist (keymap keymaps)
    (define-key keymap key cmd)))

std::eval-last-sexp

(defun std::eval-last-sexp ()
  (interactive)
  (if (and (evil-normal-state-p)
           (string= ")" (string (or (char-after) 0))))
      (save-excursion
        (forward-char)
        (call-interactively 'eval-last-sexp))
    (call-interactively 'eval-last-sexp)))

(global-set-key (kbd "C-x C-e") #'std::eval-last-sexp)
(spacemacs/set-leader-keys-for-major-mode 'emacs-lisp-mode
  "ee" #'std::eval-last-sexp)

std::org-files

(defun std::org-files ()
  (interactive)
  (helm :prompt "Org File: "
        :buffer "*helm org files*"
        :sources (helm-build-sync-source "Org Files"
                   :candidates (--map (cons (f-filename it) it) (f-files org-directory))
                   :action #'find-file-existing
                   :filtered-candidate-transformer 'helm-fuzzy-highlight-matches)))
(spacemacs/set-leader-keys
  "aof" #'std::org-files)

std::evil-paste

As shown in the spacemacs FAQ

(defun std::evil-paste-after ()
  (interactive)
  (let ((evil-this-register ?0))
    (call-interactively 'evil-paste-after)))

(defun std::evil-paste-before ()
  (interactive)
  (let ((evil-this-register ?0))
    (call-interactively 'evil-paste-before)))

(define-key evil-visual-state-map "p" #'std::evil-paste-after)
(define-key evil-visual-state-map "P" #'std::evil-paste-after)

std::fold-defun

(defun std::fold-defun ()
  (interactive)
  (save-excursion
    (end-of-line)
    (beginning-of-defun)
    (end-of-line)
    (evil-close-fold)))

(define-key evil-normal-state-map (kbd "züf") #'std::fold-defun)

Config tangle & compilation

To save on startup time the config file is only loaded on boot. Tangling and compilation is instead run as part of kill-emacs-hook, relieving the need to load all of org and tangle the config before emacs may be used.

The function to tangle and recompile the config, running only if the org file was recently modified.

(autoload #'org-babel-tangle-file "ob-tangle")

(defun std::do-recompile (org-file el-file)
  (interactive)
  (org-babel-tangle-file org-file el-file "emacs-lisp")
  (byte-recompile-file el-file t 0 nil))

(defun std::config-tangle-and-recompile ()
  (let* ((dir      (getenv "SPACEMACSDIR"))
         (el-file  (concat dir "/user-config.el"))
         (org-file (file-chase-links (concat dir "/user-config.org"))))
    (when (or (not (file-exists-p el-file))
              (time-less-p (->> el-file  (file-attributes) (nth 5))
                           (->> org-file (file-attributes) (nth 5))))
      (std::do-recompile org-file el-file))))

Advice to insert reload before spacemacs’ layer syncing.

(defun std::layer-sync-config-tangle-advice (&rest _)
  (std::config-tangle-and-recompile))

Insert retangle before both quitting emacs and reloading the config.

(add-hook 'kill-emacs-hook #'std::config-tangle-and-recompile)
(advice-add #'dotspacemacs/sync-configuration-layers :after #'std::layer-sync-config-tangle-advice)

Layer configurations

Evil

Fitting cursor state colors

(setq
 evil-normal-state-cursor   '("#ab3737" box)
 evil-insert-state-cursor   '("#33aa33" bar)
 evil-visual-state-cursor   '("#a374a8" box)
 evil-motion-state-cursor   '("#c97449" box)
 evil-operator-state-cursor '("#00688b" (hbar . 5))
 evil-emacs-state-cursor    '("#339999" bar)
 evil-resize-state-cursor   '("#ffdb1a" box))

kj key-chord

(setq-default evil-escape-key-sequence "kj")

Custom text objects

(evil-define-text-object std::evil::defun-object (count &optional beg end type)
  "Evil defun text object."
  (let ((start) (finish))
    (mark-defun)
    (setq start  (region-beginning)
          finish (region-end))
    (deactivate-mark)
    (evil-range start finish type )))

(define-key evil-operator-state-map "üf" #'std::evil::defun-object)

Settings

(setq evil-move-beyond-eol t
      evil-want-fine-undo  t
      evil-lisp-state-enter-lisp-state-on-command nil)

Elisp

Functions

std::elisp::repl-buffer

The elisp repl window refuses to be controlled by shackle, so we’ll do it manually.

(defun std::elisp::repl-buffer (&optional arg)
  (interactive "P")
  (let* ((w (get-buffer-window))
         (h (window-height w)))
    (split-window-below)
    (other-window 1)
    (-if-let (repl-buf (get-buffer "*ielm*"))
        (switch-to-buffer repl-buf)
      (ielm))
    (set-window-text-height (get-buffer-window) (round (* 0.33 h)))
    (when arg (select-window w))))

std::elisp::fold-all-top-level-forms

(defun std::elisp::fold-all-top-level-forms ()
  (interactive)
  (save-excursion
    (save-match-data
      (evil-with-state 'insert
        (goto-char (point-min))
        (while (search-forward-regexp (rx bol "(") nil t)
          (evil-close-fold))))))

Keybinds

(spacemacs/set-leader-keys-for-major-mode 'emacs-lisp-mode
  "'" #'std::elisp::repl-buffer
  "C" #'std::elisp::fold-all-top-level-forms)

Auto Completion

Global Activation

(global-company-mode t)

Settings

Company Settings

(with-eval-after-load 'company
  (setq
   company-abort-manual-when-too-short t
   company-auto-complete               nil
   company-async-timeout               10
   company-dabbrev-code-ignore-case    nil
   company-dabbrev-downcase            nil
   company-dabbrev-ignore-case         nil
   company-etags-ignore-case           nil
   company-idle-delay                  10
   company-minimum-prefix-length       2
   company-require-match               nil
   company-selection-wrap-around       t
   company-show-numbers                t
   company-tooltip-flip-when-above     nil))

Tooltip

The tooltip will look vastly better if we set a minimum width and properly align annotations.

(with-eval-after-load 'company
  (setq
   company-tooltip-minimum-width              70
   company-tooltip-align-annotations          t
   company-tooltip-margin                     2))

Backend Priority

The completions provided by combined backends should be sorted, so as to avoid interspersing semantic completion candidates with dumb code-dabbrevs

(with-eval-after-load 'company
  (defconst std::company::backend-priorities
    '((company-fish-shell   . 10)
      (company-shell        . 11)
      (company-shell-env    . 12)
      (company-anaconda     . 10)
      (company-capf         . 50)
      (company-yasnippet    . 60)
      (company-keywords     . 70)
      (company-files        . 80)
      (company-dabbrev-code . 90)
      (company-dabbrev      . 100))
    "Alist of backends' priorities.  Smaller number means higher priority.")

  (defun std::company::priority-of-backend (backend)
    "Will retrieve priority of BACKEND.
Defauts to 999 if BACKEND is nul or has no priority defined."
    (let ((pr (cdr (assoc backend std::company::backend-priorities))))
      (if (null pr) 999 pr)))

  (defun std::company::priority-compare (c1 c2)
    "Compares the priorities of C1 & C2."
    (let* ((b1   (get-text-property 0 'company-backend c1))
           (b2   (get-text-property 0 'company-backend c2))
           (p1   (std::company::priority-of-backend b1))
           (p2   (std::company::priority-of-backend b2))
           (diff (- p1 p2)))
      (< diff 0)))

  (defun std::company::sort-by-backend-priority (candidates)
    "Will sort completion CANDIDATES according to their priorities."
    (sort (delete-dups candidates) #'std::company::priority-compare)))

The priority sorting is only used in major modes which use combined backends

(defun std::company::use-completions-priority-sorting ()
  (setq-local company-transformers '(company-flx-transformer company-sort-by-occurrence std::company::sort-by-backend-priority)))

(--each '(rust-mode-hook fish-mode-hook python-mode-hook)
  (add-hook it #'std::company::use-completions-priority-sorting))

Quickhelp

Quickhelp makes company’s modemaps unreliable, so we rewrite the underlying keymap while company is active

(with-eval-after-load 'company-quickhelp

  (defun std::company::off (arg)
    "Use default keys when company is not active. ARG is ignored."
    (std::def-key-for-maps
     (kbd "C-j") #'electric-newline-and-maybe-indent
     (list evil-normal-state-map evil-insert-state-map))
    (std::def-key-for-maps
     (kbd "C-k") #'kill-line
     (list evil-normal-state-map evil-insert-state-map))
    (std::def-key-for-maps
     (kbd "C-l") #'yas-expand
     (list evil-insert-state-map)))

  (defun std::company::on (arg)
    "Use company's keys when company is active.
Necessary due to company-quickhelp using global key maps.
ARG is ignored."
    (std::def-key-for-maps
     (kbd "C-j") #'company-select-next
     (list evil-normal-state-map evil-insert-state-map))
    (std::def-key-for-maps
     (kbd "C-k") #'company-select-previous
     (list evil-normal-state-map evil-insert-state-map))
    (std::def-key-for-maps
     (kbd "C-l") #'company-quickhelp-manual-begin
     (list evil-insert-state-map)))

  (add-hook 'company-completion-started-hook   #'std::company::on)
  (add-hook 'company-completion-finished-hook  #'std::company::off)
  (add-hook 'company-completion-cancelled-hook #'std::company::off)

  (define-key company-active-map (kbd "C-l") #'company-quickhelp-manual-begin))

Company Flx

(with-eval-after-load 'company
  (company-flx-mode t)
  (setq company-flx-limit 400))

Bindings

(global-set-key (kbd "C-SPC") #'company-complete)
(global-set-key (kbd "C-@") #'company-complete)

Org

Functions

org-switch-to-buffer-other-window Org thinks it’s a good idea to disable display-buffer-alist when displaying its buffers. I don’t. I want my buffers’ display behaviour to be handled by shackle. All of them. No exceptions.

(with-eval-after-load 'org
  (defun org-switch-to-buffer-other-window (&rest args)
    "Same as the original, but lacking the wrapping
    call to `org-no-popups'"
    (apply 'switch-to-buffer-other-window args)))

std::org::capture-std-target

(with-eval-after-load 'org
  (defun std::org::capture-std-target ()
    `(file+headline
      ,(concat org-directory "Capture.org")
      ,(if (s-equals? (system-name) "a-laptop")
           "Ideen"
         "Postfach"))))

Settings

Prerequisites

(setq-default org-directory          "~/Dropbox/Org/"
              org-default-notes-file (concat org-directory "Capture.org"))

Additional modules

(with-eval-after-load 'org
  (add-to-list 'org-modules 'org-habit)
  (require 'org-habit))

Better looking TODO keywords

(with-eval-after-load 'org
  (setq-default org-todo-keywords '((sequence "[TODO]" "|" "[DONE]"))))

No `special` behaviour.

(with-eval-after-load 'org
  (setq
   org-special-ctrl-a         nil
   org-special-ctrl-k         nil
   org-special-ctrl-o         nil
   org-special-ctrl-a/e       nil
   org-ctrl-k-protect-subtree nil))

Agenda

(with-eval-after-load 'org-agenda

  (add-to-list 'org-agenda-files (concat org-directory "NT.org"))

  (pcase (system-name)
    ("a-laptop"  (add-to-list 'org-agenda-files (concat org-directory "Privat.org")))
    ("nt-laptop" (message "TODO")))

  (setq
   org-agenda-skip-scheduled-if-deadline-is-shown t
   org-agenda-span                                14
   org-agenda-window-frame-fractions              '(0.7 . 0.7)
   org-agenda-window-setup                        'current-window
   org-deadline-warning-days                      10
   org-extend-today-until                         2))

Habits

(with-eval-after-load 'org-habit
  (setq org-habit-graph-column 70
        org-habit-show-habits-only-for-today nil))

Bullets

Use only one bullet for headings (original = “◉” “○” “✸” “✿”)

(with-eval-after-load 'org
  (setq-default org-bullets-bullet-list '("")))

Also use ascii bullets for simple lists

(font-lock-add-keywords
 'org-mode
 '(("^ +\\([-*]\\) " (0 (prog1 () (compose-region (match-beginning 1) (match-end 1) ""))))))

Capture

(with-eval-after-load 'org
  (setq org-capture-templates
        `(("t" "Idee/Todo" entry
           ,(std::org::capture-std-target)
           "** [TODO] %?\n %U"))))

Other/Sort later

(with-eval-after-load 'org
  (setq
   calendar-date-style            'european
   org-tags-column                85
   org-src-window-setup           'other-window
   org-log-done                   'time
   org-ellipsis                   ""
   org-startup-align-all-tables   t
   org-startup-indented           t
   org-log-into-drawer            t
   org-startup-folded             t
   org-table-use-standard-references nil
   org-cycle-emulate-tab          t
   org-cycle-global-at-bob        nil
   org-M-RET-may-split-line       nil
   org-fontify-whole-heading-line nil
   org-catch-invisible-edits      'error
   org-refile-targets             '((nil . (:maxlevel . 10)))
   org-footnote-auto-adjust       t)

  (setq-default
   org-display-custom-times nil
   ;; org-time-stamp-formats   '("<%Y-%m-%d %a>" . "<%Y-%m-%d %a %H:%M>")
   ))
;;  org-catch-invisible-edits      'show
;;  org-fontify-whole-heading-line nil
;;  ;; org-hide-block-overlays
;;  org-hide-emphasis-markers      t
;;  org-list-indent-offset         1
;;  org-list-allow-alphabetical    nil
;;  org-src-fontify-natively       t
;;  org-startup-folded             t

Babel Languages

(with-eval-after-load 'org
  (org-babel-do-load-languages
   'org-babel-load-languages
   '((emacs-lisp . t)
     (shell      . t)))

  (with-eval-after-load 'python
    org-babel-load-languages
    '((python     . t))))

Font Locking

A small bit of custom font locking for ‘==>’

(defface std::result-face
  `((t (:foreground "#886688" :bold t)))
  "Face for '==>'.")

(font-lock-add-keywords
 'org-mode
 '(("==>" . 'std::result-face)))

Keybinds

Showing content

(with-eval-after-load 'org
  (spacemacs/set-leader-keys-for-major-mode 'org-mode
    "rr" #'org-reveal
    "rb" #'outline-show-branches
    "rc" #'outline-show-children
    "ra" #'outline-show-all))

Headline Navigation

(with-eval-after-load 'org
  (spacemacs/set-leader-keys-for-major-mode 'org-mode
    "u"   #'outline-up-heading
    "M-u" #'helm-org-parent-headings
    "j"   #'outline-next-visible-heading
    "k"   #'outline-previous-visible-heading
    "C-j" #'org-forward-heading-same-level
    "C-k" #'org-backward-heading-same-level))

Scheduling

(with-eval-after-load 'org
  (spacemacs/set-leader-keys-for-major-mode 'org-mode
    "s"  nil
    "ss" #'org-schedule
    "st" #'org-time-stamp
    "sd" #'org-deadline))

(Sub)Tree

(with-eval-after-load 'org
  (spacemacs/set-leader-keys-for-major-mode 'org-mode
    "wi" #'org-tree-to-indirect-buffer
    "wm" #'org-mark-subtree
    "wd" #'org-cut-subtree
    "wy" #'org-copy-subtree
    "wY" #'org-clone-subtree-with-time-shift
    "wp" #'org-paste-subtree
    "wr" #'org-refile))

Structure Editing

(with-eval-after-load 'org
  (dolist (mode '(normal insert))
    (evil-define-key mode org-mode-map
      (kbd "M-RET") #'org-meta-return
      (kbd "M-h")   #'org-metaleft
      (kbd "M-l")   #'org-metaright
      (kbd "M-j")   #'org-metadown
      (kbd "M-k")   #'org-metaup
      (kbd "M-H")   #'org-shiftmetaleft
      (kbd "M-L")   #'org-shiftmetaright
      (kbd "M-J")   #'org-shiftmetadown
      (kbd "M-K")   #'org-shiftmetaup
      (kbd "M-t")   #'org-insert-todo-heading-respect-content)))

Sparse Trees

(with-eval-after-load 'org
  (spacemacs/set-leader-keys-for-major-mode 'org-mode
    "7"   #'org-sparse-tree
    "8"   #'org-occur
    "M-j" #'next-error
    "M-k" #'previous-error))

Narrowing

(with-eval-after-load 'org
  ;;Spacemancs default *,n* needs to be removed first
  (spacemacs/set-leader-keys-for-major-mode 'org-mode "n" nil)

  (spacemacs/set-leader-keys-for-major-mode 'org-mode
    "nb" #'org-narrow-to-block
    "ne" #'org-narrow-to-element
    "ns" #'org-narrow-to-subtree
    "nw" #'widen))

Insert Commands

(with-eval-after-load 'org
  (spacemacs/set-leader-keys-for-major-mode 'org-mode
    "if" #'org-footnote-new
    "il" #'org-insert-link
    "in" #'org-add-note
    "id" #'org-insert-drawer
    "iD" #'org-insert-property-drawer))

Tables

(with-eval-after-load 'org
  (defun std::org::table-recalc ()
    "Reverse the prefix arg bevaviour of `org-table-recalculate', such that
by default the entire table is recalculated, while with a prefix arg recalculates
only the current cell."
    (interactive)
    (setq current-prefix-arg (not current-prefix-arg))
    (call-interactively #'org-table-recalculate))

  (defun std::org::table-switch-right ()
    "Switch content of current table cell with the cell to the right."
    (interactive)
    (when (org-at-table-p)
      (std::org::table-switch (org-table-current-line) (1+ (org-table-current-column)))))

  (defun std::org::table-switch-left ()
    "Switch content of current table cell with the cell to the left."
    (interactive)
    (when (org-at-table-p)
      (std::org::table-switch (org-table-current-line) (1- (org-table-current-column)))))

  (defun std::org::table-switch (x2 y2)
    (let* ((p  (point))
           (x1 (org-table-current-line))
           (y1 (org-table-current-column))
           (t1 (org-table-get x1 y1))
           (t2 (org-table-get x2 y2)))
      (org-table-put x1 y1 t2)
      (org-table-put x2 y2 t1 t)
      (goto-char p)))

  ;; TODO: rebind clock
  (spacemacs/set-leader-keys-for-major-mode 'org-mode "q" nil)

  (spacemacs/set-leader-keys-for-major-mode 'org-mode
    "qt"  #'org-table-create-or-convert-from-region
    "qb"  #'org-table-blank-field
    "qd"  #'org-table-delete-column
    "qc"  #'org-table-insert-column
    "qr"  #'org-table-insert-row
    "q-"  #'org-table-insert-hline
    "q0"  #'org-table-sort-lines
    "qy"  #'org-table-copy-region
    "qx"  #'org-table-cut-region
    "qp"  #'org-table-paste-rectangle
    "qo"  #'org-table-toggle-coordinate-overlays
    "qf"  #'std::org::table-recalc
    "q#"  #'org-table-rotate-recalc-marks
    "qg"  #'org-plot/gnuplot
    "qsl" #'std::org::table-switch-right
    "qsh" #'std::org::table-switch-left)

  (evil-leader/set-key-for-mode 'org-mode
    "+" #'org-table-sum
    "?" #'org-table-field-info))

Toggles

(with-eval-after-load 'org
  (spacemacs/set-leader-keys-for-major-mode 'org-mode
    "zh" #'org-toggle-heading
    "zl" #'org-toggle-link-display
    "zx" #'org-toggle-checkbox
    "zc" #'org-toggle-comment
    "zt" #'org-toggle-tag
    "zi" #'org-toggle-item
    "zo" #'org-toggle-ordered-property))

Other

(with-eval-after-load 'org
  (spacemacs/set-leader-keys-for-major-mode 'org-mode
    "0"   #'org-sort
    "#"   #'org-update-statistics-cookies
    "C-y" #'org-copy-visible
    "C-p" #'org-set-property
    "C-f" #'org-footnote-action
    "C-o" #'org-open-at-point
    "C-e" #'org-edit-special
    "P"   #'org-priority)

  (evil-define-key 'normal org-mode-map
    "-" #'org-cycle-list-bullet
    "t" #'org-todo)

  (define-key org-src-mode-map (kbd "C-x C-s") #'ignore)
  (define-key org-src-mode-map (kbd "C-c C-c") #'org-edit-src-exit))

Shell Scripts

Functions

std::fish-mode-hook

(defun std::fish::mode-hook ()
  (setq imenu-generic-expression fish-mode-imenu-expr))
(add-hook 'fish-mode-hook #'std::fish::mode-hook)

Settings

(with-eval-after-load 'company
  (setq
   company-shell-delete-duplicates nil
   company-shell-modes             nil
   company-fish-shell-modes        nil
   company-shell-use-help-arg      t))

(setq company-backends-fish-mode
      '((company-dabbrev-code company-files company-shell company-shell-env company-fish-shell :with company-yasnippet)))

(defconst fish-mode-imenu-expr
  (list
   (list
    "Function"
    (rx (group-n 1 (seq bol "function" (1+ space)))
        (group-n 2 (1+ (or alnum (syntax symbol)))) symbol-end)
    2)

   (list
    "Variables"
    (rx bol "set" (1+ space) (0+ "-" (1+ alpha) (1+ space))
        (group-n 1 symbol-start (1+ (or word "_"))))
    1)))

Helm

Functions

std::org-helm-headings

(autoload 'helm-source-org-headings-for-files "helm-org")

(defun std::org-in-buffer-headings ()
  "Slightly retooled ~helm-org-in-buffer-headings~ to have the candidates retain their fontification."
  (interactive)
  (helm :sources (helm-source-org-headings-for-files
                  (list (current-buffer)))
        :candidate-number-limit 99999
        :preselect (helm-org-in-buffer-preselect)
        :truncate-lines helm-org-truncate-lines
        :buffer "*helm org inbuffer*"))

std::helm-semantic-or-imenu

(defun std::helm-semantic-or-imenu ()
    "Same as `helm-semantic-or-imenu', but will call `std::org-helm-headings' in org-mode buffers."
    (interactive)
    (if (eq major-mode 'org-mode)
        (std::org-in-buffer-headings)
      (call-interactively #'helm-semantic-or-imenu)))

Keybinds

(spacemacs/set-leader-keys
  "hi" #'std::helm-semantic-or-imenu)

Rust

Functions

std::rust::build-rusty-tags

(with-eval-after-load 'rust-mode
  (defun std::rust::build-rusty-tags ()
    (interactive)
    (make-thread
     #'(lambda ()
         (let ((default-directory (projectile-project-root)))
           (call-process-shell-command "rusty-tags emacs")
           (call-process-shell-command "mv rusty-tags.emacs TAGS")
           (message "Rusty tags rebuilt."))))))

Keybinds

(with-eval-after-load "racer"
  (evil-define-key 'normal racer-mode-map      (kbd "M-.") #'racer-find-definition)
  (evil-define-key 'insert racer-mode-map      (kbd "M-.") #'racer-find-definition)
  (evil-define-key 'normal racer-help-mode-map (kbd "q")   #'kill-buffer-and-window)

  (spacemacs/set-leader-keys-for-major-mode 'rust-mode
    "f"   #'rust-format-buffer
    "a"   #'rust-beginning-of-defun
    "e"   #'rust-end-of-defun
    "d"   #'racer-describe
    "C-t" #'std::rust::build-rusty-tags))

Settings

Add company-dabbrev-code to front row of completion backends.

(with-eval-after-load "rust-mode"
  (setq company-backends-rust-mode
        '((company-capf :with company-dabbrev-code company-yasnippet)
          (company-dabbrev-code company-gtags company-etags company-keywords :with company-yasnippet)
          (company-files :with company-yasnippet)
          (company-dabbrev :with company-yasnippet))))

Projectile

Functions

std::projectile::magit-status

(defun std::projectile::magit-status (&optional arg)
  "Use projectile with Helm for running `magit-status'

  With a prefix ARG invalidates the cache first."
     (interactive "P")
     (if (projectile-project-p)
         (projectile-maybe-invalidate-cache arg))
     (let ((helm-ff-transformer-show-only-basename nil)
           (helm-boring-file-regexp-list           nil))
       (helm :prompt "Git status in project: "
             :buffer "*helm projectile*"
             :sources (helm-build-sync-source "Projectile Projects"
                        :candidates projectile-known-projects
                        :action #'magit-status
                        :filtered-candidate-transformer 'helm-fuzzy-highlight-matches))))

Keybinds

(with-eval-after-load 'projectile
  (spacemacs/set-leader-keys
    "pg"  nil
    "pt"  #'projectile-find-tag
    "psa" #'helm-projectile-ag
    "pgs" #'std::projectile::magit-status
    "pC"  #'projectile-cleanup-known-projects))

Settings

(with-eval-after-load 'projectile
  (setq projectile-switch-project-action #'project-find-file))

Flycheck

Settings

(setq
 flycheck-check-syntax-automatically '(mode-enabled save idle-change)
 flycheck-idle-change-delay          10
 flycheck-pos-tip-timeout            999)

Keybinds

(with-eval-after-load "flycheck"

  (evil-leader/set-key
    "ee"    #'flycheck-buffer
    "e C-e" #'flycheck-mode)

  (define-key evil-normal-state-map (kbd "C-.") #'spacemacs/next-error)
  (define-key evil-normal-state-map (kbd "C-,") #'spacemacs/previous-error))

Helm

Settings

(setq
 helm-ag-base-command              "ag -f -t --hidden --nocolor --nogroup --depth 999999 --smart-case --recurse"
 helm-imenu-delimiter              ": "
 helm-move-to-line-cycle-in-source t
 helm-swoop-use-line-number-face   t)

Keybinds

(with-eval-after-load "helm"

  (evil-leader/set-key
    "saa" #'helm-do-ag-this-file)

  (define-key helm-map (kbd "M-j") #'helm-next-source)
  (define-key helm-map (kbd "M-k") #'helm-previous-source))

Version Control

Settings

(with-eval-after-load 'magit
  (setq
   git-commit-summary-max-length              120
   magit-diff-highlight-hunk-region-functions '(magit-diff-highlight-hunk-region-dim-outside)))

(with-eval-after-load 'git-gutter
  (setq git-gutter-fr:side 'left-fringe))

Keybinds

(with-eval-after-load 'magit
  (defvar std::magit-key-maps
    (list
     magit-mode-map
     magit-status-mode-map
     magit-log-mode-map
     magit-diff-mode-map
     magit-branch-section-map
     magit-untracked-section-map
     magit-file-section-map
     magit-status-mode-map
     magit-hunk-section-map
     magit-stash-section-map
     magit-stashes-section-map
     magit-staged-section-map
     magit-unstaged-section-map))

  (std::def-key-for-maps (kbd "J")   #'std::quick-forward              std::magit-key-maps)
  (std::def-key-for-maps (kbd "K")   #'std::quick-backward             std::magit-key-maps)
  (std::def-key-for-maps (kbd "M-j") #'magit-section-forward-sibling   std::magit-key-maps)
  (std::def-key-for-maps (kbd "M-k") #'magit-section-backward-sibling  std::magit-key-maps)
  (std::def-key-for-maps (kbd ",u")  #'magit-section-up                std::magit-key-maps)
  (std::def-key-for-maps (kbd ",1")  #'magit-section-show-level-1-all  std::magit-key-maps)
  (std::def-key-for-maps (kbd ",2")  #'magit-section-show-level-2-all  std::magit-key-maps)
  (std::def-key-for-maps (kbd ",3")  #'magit-section-show-level-3-all  std::magit-key-maps)
  (std::def-key-for-maps (kbd ",4")  #'magit-section-show-level-4-all  std::magit-key-maps))

Elm

Functions

(defun std::format-and-save-elm-buffer ()
  "Format an elm buffer and then save it."
  (interactive)
  (elm-mode-format-buffer)
  (save-buffer))

Settings

(defun std::elm-mode-hook ()
  (setq-local company-backends
              '((company-elm company-dabbrev-code company-files :with company-yasnippet))))

(add-hook 'elm-mode-hook #'std::elm-mode-hook t)

Keybinds

(with-eval-after-load 'elm-mode
  (spacemacs/set-leader-keys-for-major-mode 'elm-mode
    "R"   nil
    "h"   nil
    "="   nil
    "em"  #'elm-preview-main
    "eb"  #'elm-preview-buffer
    "d"   #'elm-oracle-doc-at-point
    "t"   #'elm-oracle-type-at-point
    "=="  #'elm-mode-format-buffer
    "C-t" #'elm-mode-generate-tags)

  (which-key-add-major-mode-key-based-replacements 'elm-mode
    ",e" "preview")

  (spacemacs/set-leader-keys
    "fs" #'std::format-and-save-elm-buffer)

  (define-key elm-package-mode-map (kbd "J")       #'std::quick-forward)
  (define-key elm-package-mode-map (kbd "K")       #'std::quick-backward)
  (define-key elm-package-mode-map (kbd "q")       #'kill-buffer-and-window)
  (define-key elm-mode-map         (kbd "C-x C-s") #'std::format-and-save-elm-buffer)
  (define-key elm-mode-map         (kbd "M-.")     #'elm-mode-goto-tag-at-point))

Git

Settings

(with-eval-after-load 'magit
  (setq magit-repository-directories  '(("~/Documents/git/" . 1))
        magit-display-buffer-function 'magit-display-buffer-fullframe-status-v1))

Ranger

Functions

std::deer-new-frame

(defun std::deer-new-frame ()
  (interactive)
  (let ((frame (make-frame-command)))
    (select-frame frame)
    (deer)
    (delete-other-windows)))

std::deer-kill-frame

(with-eval-after-load 'ranger
  (defun std::deer-delete-frame ()
    (interactive)
    (ranger-close)
    (when (> (cl-list-length (frame-list)) 1)
      (delete-frame))))

Settings

(with-eval-after-load 'ranger
  (setq ranger-cleanup-eagerly    t
        ranger-cleanup-on-disable t
        ranger-deer-show-details  t
        ranger-dont-show-binary   t
        ranger-hide-cursor        t
        ranger-listing-dir-first  t
        ranger-modify-header      t
        ranger-map-style          'dired
        ranger-override-dired     t
        ranger-tabs-style         'normal
        ranger-show-hidden        t))

Keybinds

(spacemacs/set-leader-keys
  "ad" #'std::deer-new-frame)

(with-eval-after-load 'ranger
  (evil-define-key 'motion ranger-mode-map (kbd "q") #'std::deer-delete-frame))

Clojure

Settings

(setq clojure-enable-fancify-symbols t)

Single Package Configurations

dired+

(with-eval-after-load 'dired
  (require 'dired+))

Eyebrowse

Switch desktops via SPC + num

(eyebrowse-mode t)
(dolist (num (number-sequence 0 9))
  (let ((key  (kbd (concat "SPC " (number-to-string num))))
        (func (intern (concat "eyebrowse-switch-to-window-config-" (number-to-string num)))))
    (spacemacs/set-leader-keys key func)))

Shackle

Replace popwin

(shackle-mode t)

(setq helm-display-function 'pop-to-buffer)

(setq shackle-rules
      '(("*helm-ag*"              :select t   :align right :size 0.5)
        ("*helm semantic/imenu*"  :select t   :align right :size 0.4)
        ("*helm org inbuffer*"    :select t   :align right :size 0.4)
        (flycheck-error-list-mode :select nil :align below :size 0.25)
        (ert-results-mode         :select t   :align below :size 0.5)
        (calendar-mode            :select t   :align below :size 0.25)
        (racer-help-mode          :select t   :align right :size 0.5)
        (help-mode                :select t   :align right :size 0.5)
        (helpful-mode             :select t   :align right :size 0.5)
        (compilation-mode         :select t   :align right :size 0.5)
        ("*Org Select*"           :select t   :align below :size 0.33)
        ("*Org Note*"             :select t   :align below :size 0.33)
        ("*Org Links*"            :select t   :align below :size 0.2)
        (" *Org todo*"            :select t   :align below :size 0.2)
        ("*Man.*"                 :select t   :align below :size 0.5  :regexp t)
        ("*helm.*"                :select t   :align below :size 0.33 :regexp t)
        ("*Org Src.*"             :select t   :align below :size 0.5  :regexp t)))

Yasnippet

Keybinds

(with-eval-after-load "yasnippet"
  (define-key evil-insert-state-map (kbd "C-l") #'yas-expand))

Settings

(with-eval-after-load 'yasnippet
  (autoload #'f-join "f.el")
  (setq-default yas-snippet-dirs (list (f-join (getenv "SPACEMACSDIR") "snippets"))))

Enable Smartparens

Smartparens is disabled while yasnippet is expanding and editing a snippet. Whatever this was a workaround for I don’t seem affected, and I’d rather keep my electric pairs inside my snippets.

(unless (bound-and-true-p std::yasnippet::smartparens-restored)
  (if (member #'spacemacs//smartparens-disable-before-expand-snippet yas-before-expand-snippet-hook)
      (remove-hook 'yas-before-expand-snippet-hook #'spacemacs//smartparens-disable-before-expand-snippet)
    (error "Smartparens no longer disabled before yasnippet starts."))
  (if (member #'spacemacs//smartparens-restore-after-exit-snippet yas-after-exit-snippet-hook)
      (remove-hook 'yas-after-exit-snippet-hook #'spacemacs//smartparens-restore-after-exit-snippet)
    (error "Smartparens no longer restored after yasnippet ends.")))
(defvar std::yasnippet::smartparens-restored t)

i3wm-config-mode

(require 'i3wm-config-mode)

Writeroom mode

(spacemacs|add-toggle writeroom
  :mode writeroom-mode
  :documentation "Disable visual distractions."
  :evil-leader "TW")

(with-eval-after-load 'writeroom-mode
  (setq writeroom-width              120
        writeroom-extra-line-spacing 0))

Treemacs

Settings

(setq treemacs-be-evil t)
(with-eval-after-load 'treemacs
  (setq treemacs-header-function   #'treemacs--create-header-projectile
        treemacs-follow-after-init t
        treemacs-git-integration   t)
  (treemacs-follow-mode))

Keybinds

(spacemacs/set-leader-keys
  "ft"   #'treemacs-init
  "fT"   #'treemacs-projectile-init)

(global-set-key (kbd "<f8>")      #'treemacs-toggle)
(global-set-key (kbd "<C-M-tab>") #'treemacs-toggle)

Vimish fold

A fallback in case evil’s own folding is not sufficient

(define-key evil-normal-state-map (kbd "zva") #'vimish-fold-avy)
(define-key evil-normal-state-map (kbd "zvd") #'vimish-fold-delete)
(define-key evil-normal-state-map (kbd "zvv") #'vimish-fold-toggle)
(define-key evil-normal-state-map (kbd "zvz") #'vimish-fold)

Winum

Settings

(setq winum-scope 'frame-local)

Persp

Settings

(setq
 persp-add-buffer-on-after-change-major-mode      nil
 persp-add-buffer-on-find-file                    t
 persp-auto-resume-time                           0
 persp-auto-save-opt                              0
 persp-auto-save-persps-to-their-file-before-kill nil
 persp-autokill-buffer-on-remove                  nil)

Layouts

(spacemacs|define-custom-layout "@OrgDir"
  :binding "a"
  :body
  (-if-let (org-files (f-files org-directory))
      (find-file-existing (first org-files))
    (user-error "No org files ")))

Evil Goggles

Settings

(evil-goggles-mode t)
(setq evil-goggles-duration                     0.1
      evil-goggles-pulse                        t
      evil-goggles-enable-delete                t
      evil-goggles-enable-indent                t
      evil-goggles-enable-yank                  t
      evil-goggles-enable-join                  t
      evil-goggles-enable-fill-and-move         t
      evil-goggles-enable-paste                 t
      evil-goggles-enable-shift                 t
      evil-goggles-enable-surround              t
      evil-goggles-enable-commentary            t
      evil-goggles-enable-nerd-commenter        t
      evil-goggles-enable-replace-with-register t
      evil-goggles-enable-set-marker            t
      evil-goggles-enable-undo                  t
      evil-goggles-enable-redo                  t)

Helpful

Keybinds

(global-set-key (kbd "C-x ß") #'helpful-at-point)

Settings

(add-hook 'helpful-mode-hook #'evil-motion-state)

Modeline

Turn off unwanted segments

(spacemacs/toggle-mode-line-minor-modes-off)
(spaceline-toggle-purpose-off)

Don’t use Spacemacs’ evil state highlighter

(setq spaceline-highlight-face-func 'spaceline-highlight-face-evil-state)

Fonts

Functions

std::downscale

(cl-defun std::downscale (font &key char start end)
  (set-fontset-font "fontset-default" `(,(or start char) . ,(or end char))
                    (font-spec :size 12 :name font)))

Scaled Characters

Some unicode characters are displayed taller than the default text. Scrolling through such characters will sometimes cause the cursor to be jumpy. To avoid that these unicode characters need to be scaled down.

First the entirety of Font Awesome

(std::downscale "Font Awesome" :start #xf000 :end #xf2e0)

Then single characters, like those used for prettify-symbols

(std::downscale "Symbola" :char ?\⇛)
(std::downscale "Symbola" :char ?\⭢)
(std::downscale "Symbola" :char ?\⩵)
(std::downscale "Symbola" :char ?\⮕)
(std::downscale "Symbola" :char ?\⬅)
(std::downscale "Symbola" :char ?\◉)
(std::downscale "Symbola" :char ?\•)
(std::downscale "Symbola" :char ?\⏵)
(std::downscale "Symbola" :char ?\⏸)
(std::downscale "Symbola" :char ?\⏹)
(std::downscale "Symbola" :char ?\⏮)
(std::downscale "Symbola" :char ?\⏭)
(std::downscale "Symbola" :char ?\⏪)
(std::downscale "Symbola" :char ?\⏩)
(std::downscale "Symbola" :char ?\🔀)
(std::downscale "Symbola" :char ?\🔁)
(std::downscale "Symbola" :char ?\🔂)
(std::downscale "Symbola" :char ?\❯)
(std::downscale "Symbola" :char ?\✸)
(std::downscale "Symbola" :char ?\✿)
(std::downscale "Cantarell" :char ?\•)

Misc. Settings

General Key Binds

Use visual lines

(std::def-key-for-maps
 (kbd "j") #'evil-next-visual-line
 (list evil-normal-state-map evil-visual-state-map evil-motion-state-map))
(std::def-key-for-maps
 (kbd "k") #'evil-previous-visual-line
 (list evil-normal-state-map evil-visual-state-map evil-motion-state-map))

Quick line jumping

(defun std::quick-forward ()
  (interactive) (evil-next-visual-line 5))

(defun std::quick-backward ()
  (interactive) (evil-previous-visual-line 5))

(dolist (map (list evil-normal-state-map evil-visual-state-map evil-motion-state-map))
  (define-key map (kbd "J") #'std::quick-forward))

(dolist (map (list evil-normal-state-map evil-visual-state-map evil-motion-state-map))
  (define-key map (kbd "K") #'std::quick-backward))

Emacs style line start/end jump

(dolist (map (list evil-motion-state-map evil-normal-state-map evil-visual-state-map evil-insert-state-map))
  (define-key map (kbd "C-e") #'evil-end-of-visual-line))

(dolist (map (list evil-motion-state-map evil-normal-state-map evil-visual-state-map evil-insert-state-map))
  (define-key map (kbd "C-a") #'evil-beginning-of-visual-line))

Evil search on emacs’ buttons

(global-set-key (kbd "C-s") #'evil-search-forward)
(global-set-key (kbd "C-r") #'evil-search-backward)

Splitting and joining lines

(define-key evil-normal-state-map (kbd "C-j") #'electric-newline-and-maybe-indent)
(evil-leader/set-key "C-j" #'evil-join)

Dumb Refactoring

(defun std::defun-query-replace ()
  (interactive)
  (mark-defun)
  (call-interactively 'anzu-query-replace))

(evil-leader/set-key
  "üü" #'anzu-query-replace
  "üf" #'std::defun-query-replace)

Find definition

(global-set-key (kbd "M-.") #'xref-find-definitions)
(define-key evil-normal-state-map (kbd "M-.") #'xref-find-definitions)

Quitting with q

(evil-define-key 'normal messages-buffer-mode-map (kbd "q") #'quit-window)
(define-key flycheck-error-list-mode-map (kbd "q") #'kill-buffer-and-window)
(with-eval-after-load 'Man-mode
  (define-key Man-mode-map (kbd "q") #'kill-buffer-and-window))

Killing a buffer alongside its window

(spacemacs/set-leader-keys "b C-d" #'kill-buffer-and-window)

Same comment keybind as in eclipse

(global-set-key (kbd "C-7") #'evilnc-comment-operator)

Smooth Scrolling

No more jumpy recenter

(setq
  scroll-conservatively           20
  scroll-margin                   10
  scroll-preserve-screen-position t)

Minor modes

On/Off Switches

On

(global-subword-mode t)
(blink-cursor-mode t)
(mouse-avoidance-mode 'banish)
(desktop-save-mode t)

Off

(ido-mode -1)
(global-hl-line-mode -1)

Hooks

(add-hook 'prog-mode-hook       #'rainbow-delimiters-mode-enable)
(add-hook 'snippet-mode-hook    #'rainbow-delimiters-mode-disable)
(add-hook 'emacs-lisp-mode-hook #'rainbow-mode)
(add-hook 'conf-mode-hook       #'rainbow-mode)
(add-hook 'help-mode-hook       #'rainbow-mode)
(add-hook 'org-mode-hook        #'smartparens-mode)

Prettify Symbols

(setq-default
 prettify-symbols-alist
 `(("lambda" . "λ")
   ("!="     . "")
   ("=="     . "")
   ("<="     . "")
   ("=>"     . "")
   ("<-"     . "")
   ("->"     . "")))
(add-hook 'prog-mode-hook #'prettify-symbols-mode)

Single Settings

Better line numbers

(setq linum-format " %d ")

Pos Tip colors

(with-eval-after-load "pos-tip"
  (setq pos-tip-background-color "#2d2d2d"
        pos-tip-foreground-color "#ccb18b"))

Pack the custom settings away from the actual config to somewhere they can be easily gitignored.

(setq custom-file (concat (getenv "SPACEMACSDIR") "/custom-file.el"))

Simple newlines

(setq next-line-add-newlines t)

No more ugly line splitting

(setq-default truncate-lines t)

I’ll never want to keep my current tags when switching projects

(setq tags-add-tables nil)

No need for vi-tilde, emacs default looks better

(setq-default indicate-empty-lines t)

Banish mouse to the bottom so as not to interfere with notifications

(setq-default mouse-avoidance-banish-position
              '((frame-or-window . frame)
                (side . right)
                (side-pos . 3)
                (top-or-bottom . bottom)
                (top-or-bottom-pos . 0)))

Don’t load outdated files

(setq load-prefer-newer t)

1 tab = 4 spaces

(setq-default tab-width 4)

No more asking to following symlinks

(setq vc-follow-symlinks t)

Desktop Restoration

Settings

(setq desktop-restore-eager           2
      desktop-restore-forces-onscreen nil
      desktop-lazy-idle-delay         10)

Restoration

The way spacemacs loads its config doesn’t seem to mesh with desktop-save-mode so it needs to be done manually - just once on boot and not every time the config is reloaded at runtime

(when (and (bound-and-true-p desktop-save-mode)
           (not (boundp 'std::boot-finished)))
  (desktop-read)
  (defvar std::boot-finished t))

This final text prevents that the END_SRC face bleeds into the collapsed heading.