Elis Emacs configuration

Table of Contents

1 Bootstrap this configuration

This configuration is designed to follow the XDG guidelines. This means that I configure locations for files of quite a lot of things.

To use this configuration of Emacs, save this file in ~/.config/emacs/config.org. You can download the file here.

This is a minimal bootstrap which should live in ~/.emacs:

;; Define XDG directories
(defvar user-emacs-config-directory
  (concat (getenv "HOME") "/.config/emacs"))

(defvar user-emacs-data-directory
  (concat (getenv "HOME") "/.local/share/emacs"))

(defvar user-emacs-cache-directory
  (concat (getenv "HOME") "/.cache/emacs"))

;; Load config
(org-babel-load-file (concat user-emacs-config-directory "/config.org"))

2 Emacs initialization

2.1 Add package sources and initialize the package manager

Set up package sources with HTTPS, specify where to store packages and initialize the package management.

(setq-default package-archives
              '(("gnu" . "https://elpa.gnu.org/packages/")
                ("melpa" . "https://melpa.org/packages/")))

(setq-default package-user-dir (concat user-emacs-cache-directory "/elpa"))

(package-initialize)

2.2 Install use-package if missing

(unless (package-installed-p 'use-package)
  (progn
    (package-refresh-contents)
    (package-install 'use-package)))

(require 'use-package)

3 TODO Modes to try out

4 General configuration

4.1 Backup Files

Unless the $XGD_DATA_DIR/emacs/backup directory exists, create it. Then set as backup directory.

(let ((backup-dir (concat user-emacs-data-directory "/backup")))
  (unless (file-directory-p backup-dir)
    (mkdir backup-dir t))

  (setq-default backup-directory-alist (cons (cons "." backup-dir) nil)))

4.2 Lock-files

Disable creation of lock-files named .#<filename>.

(setq-default create-lockfiles nil)

4.3 auto-save-list file

Move auto-save-list files to $XDG_CACHE_DIR.

(setq-default auto-save-list-file-prefix
              (concat user-emacs-cache-directory "/auto-save-list/saves-"))

4.4 Custom vars

(setq-default indent-tabs-mode nil)    ; Always indent with spaces
(setq-default mouse-yank-at-point t)   ; Paste at cursor, not at mouse
(setq-default show-trailing-whitespace t) ; Highlight trailing whitespaces
(setq-default calendar-week-start-day 1)  ; Weeks starts on Mondays
(setq-default tab-width 4)                ; Set tab-size to 4 spaces

;; Browser
(setq-default browse-url-browser-function 'browse-url-generic)
(setq-default browse-url-generic-program
              (concat (getenv "HOME") "/bin/browser"))

4.5 Miscellaneous stuff

;; Automagic indent on newline
(global-set-key "\C-m" 'newline-and-indent)

;; Auto reread from disk when file changes
(global-auto-revert-mode t)

;; Enable line highlight mode everywhere
(global-hl-line-mode 1)

;; Make sure that there is one newline at the end of the file while saving,
;; also removes all spaces at the end of lines.
(add-hook 'before-save-hook 'delete-trailing-whitespace)

;; Change all yes-or-no-p to y-or-n-p
(defalias 'yes-or-no-p 'y-or-n-p)

4.6 Theme

(use-package zerodark-theme
  :ensure t
  :config
  (progn
    (load-theme 'zerodark t)
    (zerodark-setup-modeline-format)))

4.7 Set font

(if (string= system-name "phouchg")
    (add-to-list 'default-frame-alist '(font . "Liberation Mono 6"))
  (add-to-list 'default-frame-alist '(font . "Liberation Mono 10")))

4.8 Cursor color

(use-package frame
  :config
  (progn
    (add-hook 'after-make-frame-functions
              (lambda (frame)
                (modify-frame-parameters
                 frame (list (cons 'cursor-color "White")))))))

4.9 UI Elements

(menu-bar-mode -1)                      ; Hide menubar
(tool-bar-mode -1)                      ; Hide toolbar

(if (boundp 'scroll-bar-mode)
    (scroll-bar-mode -1))               ; Hide scrollbar

;; Hide startup screen
(setq-default inhibit-startup-screen t)

;; Highlight parenthesises
(show-paren-mode 1)

;; Enable column number in modeline
(column-number-mode 1)

4.10 Linum mode

;; Enable linum mode to show line-numbers
(setq-default linum-format "%3d ")
(add-hook 'prog-mode-hook 'linum-mode)

4.11 Unbind suspend frame

Unbind the keys for suspending emacs, these are terrible to hit when using a graphical emacs.

(global-unset-key (kbd "C-z"))
(global-unset-key (kbd "C-x C-z"))

5 Major modes

5.1 Fish shell mode   fish

(use-package fish-mode :ensure t)

5.2 Go mode   go

(use-package go-mode
  :ensure t
  :config
  (progn
    (add-hook 'before-save-hook 'gofmt-before-save)))

5.3 PHP mode   php

Install PHP-mode, set coding style and add a hook to load flymake.

(use-package php-mode
  :ensure t
  :config
  (progn
    (setq-default php-mode-coding-style 'psr2)))

5.4 SCSS mode   scss

Disable compile at save since I rarely have ruby installed.

(use-package scss-mode
  :ensure t
  :config
  (progn
    (setq-default scss-compile-at-save nil)))

5.5 Web Mode   web

Install web-mode, set rules for filetypes and indent.

(use-package web-mode
  :ensure t
  :mode "\\.twig$"
  :mode "\\.html$"
  :config
  (progn
    (setq-default web-mode-markup-indent-offset 4) ; HTML
    (setq-default web-mode-css-indent-offset 4)    ; CSS
    (setq-default web-mode-code-indent-offset 4))) ; JS/PHP/etc

5.6 Markdown

Handy when editing markdown.

(use-package markdown-mode :ensure t)

5.7 YAML

Handy when editing YAML/YML.

(use-package yaml-mode :ensure t)

5.8 Rest Client   restclient

(use-package restclient
  :ensure t
  :mode "\\.rest$"
  :config
  (progn
    ;; Add hook to override C-c C-c in this mode to stay in window
    (add-hook 'restclient-mode-hook
              '(lambda ()
                 (local-set-key
                  (kbd "C-c C-c")
                  'restclient-http-send-current-stay-in-window)))))

6 Company mode and backends   company

(use-package company
  :ensure t
  :diminish company-mode
  :bind ("<backtab>" . company-complete)
  :config
  (progn
    ;; TODO: Keybind company-complete to something good
    (setq-default company-tooltip-minimum-width 15)

    (setq-default company-idle-delay 0.1)

    (global-company-mode)))

6.1 Fuzzy Matching   flx

(use-package company-flx
  :ensure t
  :config
  (progn
    (with-eval-after-load 'company
      (company-flx-mode +1))))

6.2 Statistics for completions   statistics

(use-package company-statistics
  :ensure t
  :config
  (progn
    (setq-default company-statistics-file
                  (concat user-emacs-data-directory
                          "/company-statistics.dat"))
    (company-statistics-mode)))

6.3 Restclient Mode   restclient

(use-package company-restclient
  :ensure t
  :config
  (progn
    (add-hook 'restclient-mode-hook
              (lambda ()
                (set (make-local-variable 'company-backends)
                     '(company-restclient))

                (company-mode t)))))

6.4 PHP   php

Utilizes the program ctags in the background. Available in pacman.

(use-package company-php
  :ensure t
  :config
  (progn
    (setq-default ac-php-tags-path
                  (concat user-emacs-cache-directory "/ac-php"))

    (add-hook 'php-mode-hook
              '(lambda ()
                 (unless (executable-find "ctags")
                   (error "Program: ctags is missing"))

                 ;; Add build company-backends with dabbrev and ac-php
                 (set (make-local-variable 'company-backends)
                      '((company-dabbrev-code
                         company-gtags
                         company-etags
                         company-keywords
                         company-ac-php-backend)))

                 (company-mode t)))

    (define-key php-mode-map (kbd "C-]") 'ac-php-find-symbol-at-point)
    (define-key php-mode-map (kbd "C-t") 'ac-php-location-stack-back)))

6.5 go   go

Utilizes the program gocode as backend. Available in AUR as gocode-git.

(use-package company-go
  :ensure t
  :config
  (progn
    (add-hook 'go-mode-hook
              (lambda ()
                (unless (executable-find "gocode")
                  (error "Program: gocode is missing"))

                (set (make-local-variable 'company-backends) '(company-go))
                (company-mode t)))))

7 Utilities   utils

7.1 Anzu   anzu replace

Enables interactive replacing in emacs.

(use-package anzu
  :ensure t
  :bind (("M-%" . anzu-query-replace)
         ("C-M-%" . anzu-query-replace-regexp)))

7.2 Git frontend   magit

Install magit, bind C-x g to display the main magit popup and bind C-x M-g to display a magit popup with keybinds used in magit.

(use-package magit
  :ensure t
  :bind (("C-x g" . magit-status)     ; Display the main magit popup
         ("C-x M-g" . magit-dispatch-popup))) ; Display keybinds for magit

7.3 Flycheck   flycheck

(use-package flycheck
  :ensure t
  :diminish flycheck-mode
  :config
  (progn
    (setq-default flycheck-phpcs-standard "PSR2")
    (setq-default flycheck-php-phpcs-executable
                  (concat (getenv "HOME") "/bin/phpcs"))

    (global-flycheck-mode)))

7.4 Simple layouts   0blayout

(use-package 0blayout
  :ensure t
  :config
  (progn
    (0blayout-mode t)
    (setq-default 0blayout-default "code")))

7.5 Eyebrowse

(use-package eyebrowse
  :ensure t
  :config
  (progn
    (setq eyebrowse-new-workspace t)
    (eyebrowse-mode)))

7.6 webpaste   paste

Paste whole buffers or parts of buffers to the internet.

(use-package webpaste
  :ensure t
  :bind (("C-c C-p C-b" . webpaste-paste-buffer)
         ("C-c C-p C-r" . webpaste-paste-region))

  :config
  (progn
    (setq webpaste-open-in-browser t)))

7.7 Snippets / Templates   yasnippet

Using Yasnippet (Yet Another Snippet Extension) for snippets/templates.

(use-package yasnippet
  :ensure t
  :diminish yas-minor-mode
  :config
  (progn
    (let ((yas-dir (concat user-emacs-data-directory "/snippets")))
      (unless (file-directory-p yas-dir)
        (mkdir yas-dir t))

      (setq-default yas-snippet-dirs
                    (cons yas-dir '(yas-installed-snippets-dir))))

    (yas-global-mode 1)))

7.8 Help guide for keybinds   guide key

Enable guide-key, a mode that displays available keybinds from current state of pressed keybinds.

(use-package guide-key
  :ensure t
  :diminish guide-key-mode
  :config
  (progn
    (setq-default guide-key/guide-key-sequence t)
    (guide-key-mode 1)))

7.9 Smooth scrolling   smooth scrolling

This package makes Emacs scroll before cursor reach top or bottom which makes scrolling smoother.

(use-package smooth-scrolling
  :ensure t
  :config
  (progn
    (setq-default smooth-scroll-margin 2)
    (smooth-scrolling-mode)))

7.10 Show diff inline   diff hl

(use-package diff-hl
  :ensure t
  :config
  (progn
    (global-diff-hl-mode)))

7.11 Long lines highlight   column enforce

Also add hook to alter the limit to 77 chars for org-mode since ORG tags is located so the last character is at 76. And it looks nice to have it all aligned up.

Also add a hook to use 120 as limit for php-mode since PSR-2 states that this is the limit for php-files.

(use-package column-enforce-mode
  :ensure t
  :config
  (progn
    (face-spec-set column-enforce-face '((t (:background "dark red"))))

    ;; Make/Add hooks
    (add-hook 'prog-mode-hook 'column-enforce-mode)
    (add-hook 'php-mode-hook (make-column-rule 120))
    (add-hook 'org-mode-hook (make-column-rule 77))))

7.12 Prettify Symbols   pretty symbols

This will replace lambda with λ and some other chars in elisp-mode.

(use-package prog-mode
  :config
  (progn
    (add-hook 'emacs-lisp-mode-hook
              (lambda ()
                (push '(">=" . ?≥) prettify-symbols-alist)
                (push '("<=" . ?≤) prettify-symbols-alist)))

    (global-prettify-symbols-mode t)))

7.13 Geben   DBGp xdebug php

Enables interactive remote debugging of PHP in Emacs.

(use-package geben
  :ensure t
  :config
  (progn
    (setq-default geben-temporary-file-directory
                  (concat user-emacs-cache-directory "/geben"))))

8 Helm   helm

(use-package helm
  :diminish helm-mode
  :ensure t
  :bind (("C-x C-f" . helm-find-files)
         ("M-x" . helm-M-x)
         ("C-x b" . helm-mini)
         ("C-x C-b" . helm-mini)
         ("M-y" . helm-show-kill-ring)
         :map helm-map
         ("<tab>" . helm-execute-persistent-action) ; Rebind TAB to expand
         ("C-i" . helm-execute-persistent-action) ; Make TAB work in CLI
         ("C-z" . helm-select-action)) ; List actions using C-z
  :config
  (progn
    (setq-default helm-split-window-in-side-p t)
    (helm-mode 1)))

8.1 Helm Projectile   projectile ag

(use-package helm-projectile
  :ensure t
  :bind (("C-x , p" . helm-projectile-switch-project)
         ("C-x , f" . helm-projectile-find-file)
         ("C-x , b" . projectile-ibuffer)
         ("C-x , i" . projectile-invalidate-cache)
         ("C-x , a" . helm-projectile-ag))
  :config
  (progn
    (use-package helm-ag :ensure t)

    (projectile-mode)
    (setq-default projectile-enable-caching t)))

8.2 Helm fuzzier   fuzzy

(use-package helm-fuzzier
  :ensure t
  :config
  (progn
    (setq-default helm-mode-fuzzy-match t)
    (setq-default helm-M-x-fuzzy-match t)
    (setq-default helm-buffers-fuzzy-match t)
    (setq-default helm-recentf-fuzzy-match t)

    (helm-fuzzier-mode 1)))

8.3 Swiper Helm   swiper

(use-package swiper-helm
  :ensure t
  :bind (("C-s" . swiper-helm)
         ("C-r" . swiper-helm))
  :config
  (progn
    (setq-default ivy-use-virtual-buffers t)))

9 Org   org

Highlight code in native language in code-blocks in org-files.

(setq-default org-src-fontify-natively t)

9.1 Note capturing   capture

Define org-files/directories, templates for org-capturing of notes. More examples of templates here.

(use-package org
  :bind ("M-n" . org-capture)
  :config
  (progn
    (setq-default org-directory
                  (concat (getenv "HOME") "/org"))
    (setq-default org-default-notes-file
                  (concat org-directory "/notes.org"))

    (setq-default
     org-capture-templates
     '(("n" "Quick Notes" entry
        (file+headline org-default-notes-file "Quick Notes")
        "* %^{Note}")
       ("i" "IX Notes" entry
        (file+headline (concat org-directory "/ix.org") "IX Notes")
        "* %^{Note}")))))

9.2 Workhour Clocking   clocktable

(use-package org
  :config
  (progn
    ;; This changes formating of time in clocktables
    ;; So instead of "Dd HH:MM" we get "HH.MM"
    (setq-default org-time-clocksum-use-fractional t)


    ;; Define function to re-do indent of items in clocktable
    ;; http://emacs.stackexchange.com/a/9544
    (defun my/org-clocktable-indent-string (level)
      (if (= level 1)
          ""
        (let ((str " "))
          (while (> level 2)
            (setq level (1- level)
                  str (concat str "-")))
          (concat str "- "))))

    ;; Add advice to override indention
    (advice-add 'org-clocktable-indent-string
                :override #'my/org-clocktable-indent-string)))

9.3 Graphs   gnuplot

(use-package gnuplot
  :ensure t
  :bind (("M-C-g" . org-plot/gnuplot)))

9.4 Stylesheets / Header   publish

Shamelessly ripped from Better Motherfucking Website and altered to make code fit better.

(setq-default org-html-head "<style type=\"text/css\">body{margin:40px auto
                             ;max-width:920px; line-height:1.6;
                             font-size:18px; color:#444; padding:0 10px}
                             h1,h2,h3{line-height:1.2}</style>")

9.5 Code highlight   publish htmlize

(use-package htmlize :ensure t)

10 Useless utilities

10.1 Nyan-mode

(use-package nyan-mode :ensure t)

11 Functions

11.1 Three-column-layout

(defun three-column-layout ()
  "Split into three equaly sized columns"
  (interactive)
  (let ((new-size (/ (window-total-width) 3)))
    (split-window-right new-size)
    (other-window 1)
    (split-window-right new-size)
    (other-window -1)

    (message "Splitted to three-column-layout")))

11.2 Toggle color theme

(if (string= window-system "x")
    (progn
      (defun toggle-color-theme ()
        (interactive)
        (if (equal (car custom-enabled-themes) 'tango-dark)
            (load-theme 'adwaita)
          (load-theme 'tango-dark)))

      (global-set-key [f12] 'toggle-color-theme)))

11.3 PHP Generate constructor   php

(defun my/php-constructor ()
  "Function that searches trough the buffer and locates all Class
Dependendencies in a PHP Class and creates a __construct for the class"
  (interactive)

  ;; Go to beginning of buffer
  (goto-char (point-min))

  (let ((injections '())
        (dep-count (how-many "@var\s+"))
        point)

    ;; Loop the amount of times we found @var\s+ to collect the deps
    (dotimes (i dep-count)
      (add-to-list 'injections (my/php-find-injection)))

    ;; Move to end of line
    (move-end-of-line nil)

    ;; Make newlines for new constructor
    (newline)
    (newline)

    ;; Save point
    (setq point (point))

    ;; Insert constructor
    (insert "public function __construct(")
    (newline)

    ;; Insert constructor signature
    (dolist (injection injections)
      (insert (concat (car injection) " "))
      (insert (concat "$" (cdr injection) ","))
      (newline))

    ;; Remove the last newline and ,
    (delete-char -2)

    ;; Then create a new newline
    (newline)

    ;; Insert params
    (insert ") {")
    (newline)

    ;; Insert function content
    (dolist (injection injections)
      (insert (concat "$this->"
                      (cdr injection)
                      " = $"
                      (cdr injection)
                      ";"))
      (newline))

    ;; End function
    (insert "}")

    (indent-region point (point))))

(defun my/php-find-injection ()
  "Function that locates an Class Dependency in a PHP class"
  (let (pt php-class-name php-var-name)
    (search-forward-regexp "@var\s+")   ; Search for docblock
    (setq pt (point))                   ; Save point
    (skip-chars-forward "-_A-Za-z0-9")  ; Go to end of classname

    ;; Save classname to var
    (setq php-class-name (buffer-substring-no-properties pt (point)))

    (search-forward-regexp "private \\$") ; Search for variable
    (setq pt (point))                   ; Save point
    (skip-chars-forward "-_A-Za-z0-9$") ; Go to end of variable name

    ;; Save variable name to var
    (setq php-var-name (buffer-substring-no-properties pt (point)))

    (cons php-class-name php-var-name)))

12 Work utilities

(let ((work-lisp-dir (expand-file-name "tvnu/.lisp" (getenv "HOME"))))
  (if (file-directory-p work-lisp-dir)
      (progn
        ;; Add work lisp dir to load path
        (add-to-list 'load-path work-lisp-dir)

        (require 'work-init))))

Created: 2017-06-13 tis 09:26

Emacs 25.2.1 (Org mode 8.2.10)

Validate