Skip to content

Latest commit

 

History

History
1407 lines (1139 loc) · 43 KB

File metadata and controls

1407 lines (1139 loc) · 43 KB

My GNU Emacs configuration

Introduction

This is my version of an configuration file for GNU Emacs. I have been using GNU Emacs since 2000 when I was introduced to it by my mentor at Personify Inc. Sadly Personify Inc was victim to the dot com bust of 2001. I have been using GNU Emacs for everything since then though.

You can find the latest version of this configuration at https://github.com/credmp/emacs-config or at https://gitlab.com/buildfunthings/emacs-config.

I modelled this set of files based off of the emacs-starter-kit at https://github.com/eschulte/emacs24-starter-kit.

(setq user-full-name "Arjen Wiersma")
(setq user-mail-address "arjen@wiersma.org")

A secure Emacs environment

Great article why Your editor is malware. The following basically sets up the configuration to adhere to the articles recommondations.

;;  (if (fboundp 'gnutls-available-p)
;;      (fmakunbound 'gnutls-available-p))

(require 'cl)
(setq tls-checktrust t)

(setq python (or (executable-find "py.exe")
                 (executable-find "python")
                 ))

(let ((trustfile
       (replace-regexp-in-string
        "\\\\" "/"
        (replace-regexp-in-string
         "\n" ""
         (shell-command-to-string (concat python " -m certifi"))))))
  (setq tls-program
        (list
         (format "gnutls-cli%s --x509cafile %s -p %%p %%h"
                 (if (eq window-system 'w32) ".exe" "") trustfile)))
  (setq gnutls-verify-error t)
  (setq gnutls-trustfiles (list trustfile)))

;; Test the settings by using the following code snippet:
;;  (let ((bad-hosts
;;         (loop for bad
;;               in `("https://wrong.host.badssl.com/"
;;                    "https://self-signed.badssl.com/")
;;               if (condition-case e
;;                      (url-retrieve
;;                       bad (lambda (retrieved) t))
;;                    (error nil))
;;               collect bad)))
;;    (if bad-hosts
;;        (error (format "tls misconfigured; retrieved %s ok" bad-hosts))
;;      (url-retrieve "https://badssl.com"
;;                    (lambda (retrieved) t))))

Previous customizations

(setq custom-file (concat init-dir "custom.el"))

(load custom-file :noerror)

Installing use-package

Setup

GNU Emacs has a lot of packages that make things very easy for the user. The package management infrastructure makes it a breeze to install new packages and keep up-to-date with their development.

There are several package archives available for GNU Emacs:

  • GNU
  • Marmalade
  • Melpa

Each archive will write its files in a seperate archive directory.

(require 'package)

Because the default setting for package-archives is to use the HTTP access to the GNU archive, I set the variable to `nil` before adding the HTTPS variants.

(defvar gnu '("gnu" . "https://elpa.gnu.org/packages/"))
(defvar melpa '("melpa" . "https://melpa.org/packages/"))
(defvar melpa-stable '("melpa-stable" . "https://stable.melpa.org/packages/"))

;; Add marmalade to package repos
(setq package-archives nil)
(add-to-list 'package-archives melpa-stable t)
(add-to-list 'package-archives melpa t)
(add-to-list 'package-archives gnu t)

Initialize the archive and refresh the contents in case there is no cached archive.

(package-initialize)

(unless (and (file-exists-p (concat init-dir "elpa/archives/gnu"))
             (file-exists-p (concat init-dir "elpa/archives/melpa"))
             (file-exists-p (concat init-dir "elpa/archives/melpa-stable")))
  (package-refresh-contents))

At each load the package list will be evaluated and any missing packages will be installed. The packages-install function deals with this check and takes care of any loading of the packages.

(defun packages-install (&rest packages)
  (message "running packages-install")
  (mapc (lambda (package)
          (let ((name (car package))
                (repo (cdr package)))
            (when (not (package-installed-p name))
              (let ((package-archives (list repo)))
                (package-initialize)
                (package-install name)))))
        packages)
  (package-initialize)
  (delete-other-windows))

The package

;; Install extensions if they're missing
(defun init--install-packages ()
  (message "Lets install some packages")
  (packages-install
   ;; Since use-package this is the only entry here
   ;; ALWAYS try to use use-package!
   (cons 'use-package melpa)
   ))

(condition-case nil
    (init--install-packages)
  (error
   (package-refresh-contents)
   (init--install-packages)))

Moving around

Be sure to just ask for y/n instead of yes/no.

(fset 'yes-or-no-p 'y-or-n-p)

Bookmarks are very useful for quickly jumping around files.

(use-package bm
  :ensure t
  :bind (("C-c =" . bm-toggle)
         ("C-c [" . bm-previous)
         ("C-c ]" . bm-next)))
 (use-package counsel
   :ensure t
   :bind
   (("M-x" . counsel-M-x)
    ("M-y" . counsel-yank-pop)
    :map ivy-minibuffer-map
    ("M-y" . ivy-next-line)))

  (use-package swiper
    :pin melpa-stable
    :diminish ivy-mode
    :ensure t
    :bind*
    (("C-s" . swiper)
     ("C-c C-r" . ivy-resume)
     ("C-x C-f" . counsel-find-file)
     ("C-c h f" . counsel-describe-function)
     ("C-c h v" . counsel-describe-variable)
     ("C-c i u" . counsel-unicode-char)
     ("M-i" . counsel-imenu)
     ("C-c g" . counsel-git)
     ("C-c j" . counsel-git-grep)
     ("C-c k" . counsel-ag)
     ("C-c l" . scounsel-locate))
    :config
    (progn
      (ivy-mode 1)
      (setq ivy-use-virtual-buffers t)
      (define-key read-expression-map (kbd "C-r") #'counsel-expression-history)
      (ivy-set-actions
	'counsel-find-file
	'(("d" (lambda (x) (delete-file (expand-file-name x)))
          "delete"
          )))
      (ivy-set-actions
	'ivy-switch-buffer
	'(("k"
          (lambda (x)
            (kill-buffer x)
            (ivy--reset-state ivy-last))
          "kill")
         ("j"
          ivy--switch-buffer-other-window-action
          "other window")))))

 (use-package counsel-projectile
   :ensure t
   :config
   (counsel-projectile-on))

 (use-package ivy-hydra :ensure t)

From Pragmatic Emacs a more concise way to kill the buffer.

(global-set-key (kbd "C-x k") 'kill-this-buffer)

Mousewheel scrolling can be quite annoying, lets fix it to scroll smoothly.

(setq mouse-wheel-scroll-amount '(1 ((shift) . 1) ((control) . nil)))
(setq mouse-wheel-progressive-speed nil)

Discover-ability

(use-package which-key
  :ensure t
  :diminish which-key-mode
  :config
  (which-key-mode))

Environment

(if (or
     (eq system-type 'darwin)
     (eq system-type 'berkeley-unix))
    (setq system-name (car (split-string system-name "\\."))))

(setenv "PATH" (concat "/usr/local/bin:" (getenv "PATH")))
(push "/usr/local/bin" exec-path)

;; /usr/libexec/java_home
;;(setenv "JAVA_HOME" "/Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home")

GUI

  • Turn off mouse interface early in startup to avoid momentary display.
(menu-bar-mode 1)
(tool-bar-mode -1)
(scroll-bar-mode -1)
  • change command to meta, and ignore option to use weird Norwegian

keyboard

(setq mac-option-modifier 'none)
(setq mac-command-modifier 'meta)
(setq ns-function-modifier 'hyper)
  • Move to trash when deleting stuff and write backup files to own directory
;; Backup settings
(defvar --backup-directory (concat init-dir "backups"))

(if (not (file-exists-p --backup-directory))
    (make-directory --backup-directory t))

(setq backup-directory-alist `(("." . ,--backup-directory)))
(setq make-backup-files t               ; backup of a file the first time it is saved.
      backup-by-copying t               ; don't clobber symlinks
      version-control t                 ; version numbers for backup files
      delete-old-versions t             ; delete excess backup files silently
      delete-by-moving-to-trash t
      kept-old-versions 6               ; oldest versions to keep when a new numbered backup is made (default: 2)
      kept-new-versions 9               ; newest versions to keep when a new numbered backup is made (default: 2)
      auto-save-default t               ; auto-save every buffer that visits a file
      auto-save-timeout 20              ; number of seconds idle time before auto-save (default: 30)
      auto-save-interval 200            ; number of keystrokes between auto-saves (default: 300)
      )
  (setq delete-by-moving-to-trash t
        trash-directory "~/.Trash/emacs")

  (setq backup-directory-alist `(("." . ,(expand-file-name
                                          (concat init-dir "backups")))))
  • Don’t open files from the workspace in a new frame
(setq ns-pop-up-frames nil)

Spellchecking in Emacs. Hunspell is widely used in text editor and even as the source of the spell check in MacOS X.

Install using the Homebrew project:

Install dictionaries from the Mozilla Add-on page.

(defun spell-buffer-dutch ()
  (interactive)
  (ispell-change-dictionary "en")
  (flyspell-buffer))

(use-package ispell
  :config
  (when (executable-find "hunspell")
    (setq-default ispell-program-name "hunspell")
    (setq ispell-really-hunspell t))

  ;; (setq ispell-program-name "aspell"
  ;;       ispell-extra-args '("--sug-mode=ultra"))
  :bind ("C-c N" . spell-buffer-dutch))
  • Find out what face is used, so you can customize it :)
;;; what-face to determine the face at the current point
(defun what-face (pos)
  (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" pos))))
  • Windows management
(use-package ace-window
  :ensure t
  :config
  (global-set-key (kbd "C-x o") 'ace-window))

(use-package ace-jump-mode
  :ensure t
  :config
  (define-key global-map (kbd "C-c SPC") 'ace-jump-mode))
  • Misc stuff
;; Custom binding for magit-status
(use-package magit
  :config
  (global-set-key (kbd "C-c m") 'magit-status))

(setq inhibit-startup-message t)
(global-linum-mode)

(defun iwb ()
  "indent whole buffer"
  (interactive)
  (delete-trailing-whitespace)
  (indent-region (point-min) (point-max) nil)
  (untabify (point-min) (point-max)))

(global-set-key (kbd "C-c n") 'iwb)

(electric-pair-mode t)

Look and feel

(use-package arjen-grey-theme
  :ensure t
  :config
  (load-theme 'arjen-grey t))

(if (eq system-type 'darwin)
    (set-face-attribute 'default nil :font "Hack-14")
  (set-face-attribute 'default nil :font "DejaVu Sans Mono" :height 110))
(use-package command-log-mode
  :ensure t)

(defun live-coding ()
  (interactive)
  (set-face-attribute 'default nil :font "Hack-16")
  (add-hook 'prog-mode-hook 'command-log-mode))
(eval-after-load "org-indent" '(diminish 'org-indent-mode))

Have the ability to use some amazing font icons

;;   (use-package all-the-icons
;;     :ensure t)

Be sure to install the fonts from the github repo.

Now, lets make sure we are not `ding`-ed all the time.

;; http://stackoverflow.com/questions/11679700/emacs-disable-beep-when-trying-to-move-beyond-the-end-of-the-document
(defun my-bell-function ())

(setq ring-bell-function 'my-bell-function)
(setq visible-bell nil)

Project mappings

;;; Setup perspectives, or workspaces, to switch between
(use-package perspective
  :ensure t
  :config
  ;; Enable perspective mode
  (persp-mode t)
  (defmacro custom-persp (name &rest body)
    `(let ((initialize (not (gethash ,name perspectives-hash)))
           (current-perspective persp-curr))
       (persp-switch ,name)
       (when initialize ,@body)
       (setq persp-last current-perspective)))

  ;; Jump to last perspective
  (defun custom-persp-last ()
    (interactive)
    (persp-switch (persp-name persp-last)))

  (define-key persp-mode-map (kbd "C-x p -") 'custom-persp-last)

  (defun custom-persp/emacs ()
    (interactive)
    (custom-persp "emacs"
                  (find-file (concat init-dir "init.el"))))

  (define-key persp-mode-map (kbd "C-x p e") 'custom-persp/emacs)

  (defun custom-persp/fsc-backend ()
    (interactive)
    (custom-persp "backend"
                  (find-file "/Users/arjen/Books/source/my-brain-game/project.clj")))

  (define-key persp-mode-map (kbd "C-x p q") 'custom-persp/fsc-backend)

  (defun custom-persp/trivia ()
    (interactive)
    (custom-persp "trivia"
                  (find-file "/Users/arjen/BuildFunThings/Projects/Clojure/trivia/project.clj")))

  (define-key persp-mode-map (kbd "C-x p t") 'custom-persp/trivia)

  (defun custom-persp/typo ()
    (interactive)
    (custom-persp "typo"
                  (find-file "/Users/arjen/BuildFunThings/Projects/Clojure/typo/project.clj")))

  (define-key persp-mode-map (kbd "C-x p y") 'custom-persp/typo)
  )

Writing

Publishing

Based on the work describe here.

Support to make an external call to LeanPub.

(use-package request
  :ensure t)

You will need the ox-leanpub library from GitHub.

Now for the leanpub-export.

;;(add-to-list 'load-path (expand-file-name (concat init-dir "ox-leanpub")))
;;(load-library "ox-leanpub")
(add-to-list 'load-path (expand-file-name (concat init-dir "ox-ghost")))
(load-library "ox-ghost")
;;; http://www.lakshminp.com/publishing-book-using-org-mode

;;(defun leanpub-export ()
;;  "Export buffer to a Leanpub book."
;;  (interactive)
;;  (if (file-exists-p "./Book.txt")
;;      (delete-file "./Book.txt"))
;;  (if (file-exists-p "./Sample.txt")
;;      (delete-file "./Sample.txt"))
;;  (org-map-entries
;;   (lambda ()
;;     (let* ((level (nth 1 (org-heading-components)))
;;            (tags (org-get-tags))
;;            (title (or (nth 4 (org-heading-components)) ""))
;;            (book-slug (org-entry-get (point) "TITLE"))
;;            (filename
;;             (or (org-entry-get (point) "EXPORT_FILE_NAME") (concat (replace-regexp-in-string " " "-" (downcase title)) ".md"))))
;;       (when (= level 1) ;; export only first level entries
;;         ;; add to Sample book if "sample" tag is found.
;;         (when (or (member "sample" tags)
;;                   ;;(string-prefix-p "frontmatter" filename) (string-prefix-p "mainmatter" filename)
;;                   )
;;           (append-to-file (concat filename "\n\n") nil "./Sample.txt"))
;;         (append-to-file (concat filename "\n\n") nil "./Book.txt")
;;         ;; set filename only if the property is missing
;;         (or (org-entry-get (point) "EXPORT_FILE_NAME")  (org-entry-put (point) "EXPORT_FILE_NAME" filename))
;;         (org-leanpub-export-to-markdown nil 1 nil)))) "-noexport")
;;  (org-save-all-org-buffers)
;;  nil
;;  nil)
;;
;;(require 'request)
;;
;;(defun leanpub-preview ()
;;  "Generate a preview of your book @ Leanpub."
;;  (interactive)
;;  (request
;;   "https://leanpub.com/clojure-on-the-server/preview.json" ;; or better yet, get the book slug from the buffer
;;   :type "POST"                                             ;; and construct the URL
;;   :data '(("api_key" . ""))
;;   :parser 'json-read
;;   :success (function*
;;             (lambda (&key data &allow-other-keys)
;;               (message "Preview generation queued at leanpub.com.")))))

Word wrapping

Please wrap text around when in text-modes. Also enable flyspell to catch nasty writing errors.

(dolist (hook '(text-mode-hook))
  (add-hook hook (lambda ()
                   (flyspell-mode 1)
                   (visual-line-mode 1)
                   )))

Markdown support

Markdown is a great way to write documentation, not as good as org-mode of course, but generally accepted as a standard.

(use-package markdown-mode
  :ensure t)

HTMLize buffers

When exporting documents to HTML documents, such as code fragments, we need to htmlize.

(use-package htmlize
  :ensure t)

Exports

Export ORG code fragments with a particular theme.

(defun my/with-theme (theme fn &rest args)
  (let ((current-themes custom-enabled-themes))
    (mapcar #'disable-theme custom-enabled-themes)
    (load-theme theme t)
    (let ((result (apply fn args)))
      (mapcar #'disable-theme custom-enabled-themes)
      (mapcar (lambda (theme) (load-theme theme t)) current-themes)
      result)))

;;(advice-add #'org-export-to-file :around (apply-partially #'my/with-theme 'arjen-grey))
;;(advice-add #'org-export-to-buffer :around (apply-partially #'my/with-theme 'arjen-grey))

Enable graphing with dot and ditaa

(org-babel-do-load-languages
 'org-babel-load-languages
 '((ditaa . t)
   (dot . t)))

(setq org-ditaa-jar-path "/usr/local/Cellar/ditaa/0.9/libexec/ditaa0_9.jar")
(setq org-ditaa-eps-jar-path "/usr/local/Cellar/ditaa/0.9/libexec/ditaa0_9.jar")

(setq ditaa-cmd "java -jar <path-to-ditaa>ditaa0_6b.jar")
(defun djcb-ditaa-generate ()
  (interactive)
  (shell-command
   (concat ditaa-cmd " " buffer-file-name)))

Programming

General programming

As I write a lot of Lisp like code, either in GNU Emacs or in Clojure I like to have my environment setup for these languages. This is greatly supported by Paredit. Dan Midwood has a great guide to using paredit.

The structured editing of paredit is usefull in a LOT of languages, as long as there are parenthesis, brackets or quotes.

Utilities

String manipulation routines for emacs lisp

(use-package s
  :ensure t)

Hydras are the most awesome thing in the world. Check out the project page for some great examples.

(use-package hydra
  :ensure t)

Code Folding

(use-package hideshow
  :ensure t
  :bind (("C->" . my-toggle-hideshow-all)
         ("C-<" . hs-hide-level)
         ("C-;" . hs-toggle-hiding))
  :config
  ;; Hide the comments too when you do a 'hs-hide-all'
  (setq hs-hide-comments nil)
  ;; Set whether isearch opens folded comments, code, or both
  ;; where x is code, comments, t (both), or nil (neither)
  (setq hs-isearch-open 'x)
  ;; Add more here


  (setq hs-set-up-overlay
        (defun my-display-code-line-counts (ov)
          (when (eq 'code (overlay-get ov 'hs))
            (overlay-put ov 'display
                         (propertize
                          (format " ... <%d>"
                                  (count-lines (overlay-start ov)
                                               (overlay-end ov)))
                          'face 'font-lock-type-face)))))

  (defvar my-hs-hide nil "Current state of hideshow for toggling all.")
       ;;;###autoload
  (defun my-toggle-hideshow-all () "Toggle hideshow all."
         (interactive)
         (setq my-hs-hide (not my-hs-hide))
         (if my-hs-hide
             (hs-hide-all)
           (hs-show-all)))

  (add-hook 'prog-mode-hook (lambda ()
                              (hs-minor-mode 1)
                              ))
  (add-hook 'clojure-mode-hook (lambda ()
                              (hs-minor-mode 1)
                              ))
  )

Time Management

;;   (defun read-wakatime-api-key ()
;;     "Read the wakatime api key from .wakatime"
;;     (with-temp-buffer
;;       (insert-file-contents-literally "~/.wakatime")
;;       (s-trim (buffer-substring-no-properties (point-min) (point-max)))))
;; 
;;   (use-package wakatime-mode
;;     :if (eq system-type 'darwin)
;;     :diminish wakatime-mode
;;     :ensure t
;;     :config
;;     (setq wakatime-api-key (read-wakatime-api-key))
;;     (setq wakatime-cli-path "/usr/local/bin/wakatime")
;;     (global-wakatime-mode))

Look and feel

Enable the prettify symbols mode. It will translate (fn) to the lambda sign.

(global-prettify-symbols-mode 1)

LISP Editing

(use-package paredit
  :ensure t
  :diminish paredit-mode
  :config
  (add-hook 'emacs-lisp-mode-hook       #'enable-paredit-mode)
  (add-hook 'eval-expression-minibuffer-setup-hook #'enable-paredit-mode)
  (add-hook 'ielm-mode-hook             #'enable-paredit-mode)
  (add-hook 'lisp-mode-hook             #'enable-paredit-mode)
  (add-hook 'lisp-interaction-mode-hook #'enable-paredit-mode)
  (add-hook 'scheme-mode-hook           #'enable-paredit-mode)
  :bind (("C-c d" . paredit-forward-down))
  )

;; Ensure paredit is used EVERYWHERE!
(use-package paredit-everywhere
  :ensure t
  :diminish paredit-everywhere-mode
  :config
  (add-hook 'prog-mode-hook #'paredit-everywhere-mode))

(use-package highlight-parentheses
  :ensure t
  :diminish highlight-parentheses-mode
  :config
  (add-hook 'emacs-lisp-mode-hook
            (lambda()
              (highlight-parentheses-mode)
              )))

(use-package rainbow-delimiters
  :ensure t
  :config
  (add-hook 'lisp-mode-hook
            (lambda()
              (rainbow-delimiters-mode)
              )))

(global-highlight-parentheses-mode)

Snippets

(use-package yasnippet
  :ensure t
  :diminish yas
  :config
  (yas/global-mode 1)
  (add-to-list 'yas-snippet-dirs (concat init-dir "snippets")))

(use-package clojure-snippets
  :ensure t)

Auto completion

  (use-package company
    :ensure t
    :bind (("C-c /". company-complete))
    :config
    (global-company-mode)
    )

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

Version Control

Magit is the only thing you need when it comes to Version Control (Git)

(use-package magit
  :ensure t
  :bind (("C-c m" . magit-status)))

(use-package magit-gitflow
  :ensure t
  :config
  (add-hook 'magit-mode-hook 'turn-on-magit-gitflow))

Display the buffer state in the fringe.

;; 2017-01-01 - weird performance issue with git-gutter
;;  (use-package git-gutter-fringe
;;    :ensure t
;;    :diminish git-gutter-mode
;;    :config
;;    (setq git-gutter-fr:side 'right-fringe)
;;    (set-face-foreground 'git-gutter-fr:modified "#63747c")
;;    (set-face-foreground 'git-gutter-fr:added    "#63747c")
;;    (set-face-foreground 'git-gutter-fr:deleted  "#63747c")
;;    (global-git-gutter-mode +1))

REST support

(use-package restclient
  :ensure t)

Clojure

The clojure ecosystem for GNU Emacs consists out of CIDER and bunch of supporting modules.

CIDER

(use-package cider
  :ensure t
  :pin melpa-stable

  :config
  (add-hook 'cider-repl-mode-hook #'company-mode)
  (add-hook 'cider-mode-hook #'company-mode)
  (add-hook 'cider-mode-hook #'eldoc-mode)
  (add-hook 'cider-mode-hook #'cider-hydra-mode)
  (add-hook 'clojure-mode-hook #'paredit-mode)
  (setq cider-repl-use-pretty-printing t)
  (setq cider-repl-display-help-banner nil)
  (setq cider-cljs-lein-repl "(do (use 'figwheel-sidecar.repl-api) (start-figwheel!) (cljs-repl))")

  :bind (("M-r" . cider-namespace-refresh)
         ("C-c r" . cider-repl-reset)
         ("C-c ." . cider-reset-test-run-tests))
  )

(use-package clj-refactor
  :ensure t
  :config
  (add-hook 'clojure-mode-hook (lambda ()
                                 (clj-refactor-mode 1)
                                 ;; insert keybinding setup here
                                 ))
  (cljr-add-keybindings-with-prefix "C-c C-m")
  (setq cljr-warn-on-eval nil)
  :bind ("C-c '" . hydra-cljr-help-menu/body)
)

Cider Support Functions

Some support functions to help with the connection between the buffer and the REPL. Big caveat you need to fix here is the hard-coded cider-repl-reset, which should be project specific.

!TODO! fix this.

(defun cider-repl-command (cmd)
  "Execute commands on the cider repl"
  (cider-switch-to-repl-buffer)
  (goto-char (point-max))
  (insert cmd)
  (cider-repl-return)
  (cider-switch-to-last-clojure-buffer))

(defun cider-repl-reset ()
  "Assumes reloaded + tools.namespace is used to reload everything"
  (interactive)
  (save-some-buffers)
  (cider-repl-command "(trivia.core/reset)"))

(defun cider-reset-test-run-tests ()
  (interactive)
  (cider-repl-reset)
  (cider-test-run-project-tests))

Hydras

Retrieve the Cider-Hydra package from GitHub.

(load-library (concat init-dir "cider-hydra.el"))
(require 'cider-hydra)

Web editing

The web-mode is particularily good for editing HTML and JS files.

(use-package web-mode
  :ensure t
  :config
  (add-to-list 'auto-mode-alist '("\\.phtml\\'" . web-mode))
  (add-to-list 'auto-mode-alist '("\\.tpl\\.php\\'" . web-mode))
  (add-to-list 'auto-mode-alist '("\\.jsp\\'" . web-mode))
  (add-to-list 'auto-mode-alist '("\\.as[cp]x\\'" . web-mode))
  (add-to-list 'auto-mode-alist '("\\.erb\\'" . web-mode))
  (add-to-list 'auto-mode-alist '("\\.mustache\\'" . web-mode))
  (add-to-list 'auto-mode-alist '("\\.djhtml\\'" . web-mode))
  (add-to-list 'auto-mode-alist '("\\.html?\\'" . web-mode))
  (add-to-list 'auto-mode-alist '("\\.xhtml?\\'" . web-mode))

  (defun my-web-mode-hook ()
    "Hooks for Web mode."
    (setq web-mode-enable-auto-closing t)
    (setq web-mode-enable-auto-quoting t) 
    (setq web-mode-markup-indent-offset 2))

  (add-hook 'web-mode-hook  'my-web-mode-hook))

(use-package less-css-mode
  :ensure t)

(use-package emmet-mode
  :ensure t
  :config
  (add-hook 'clojure-mode-hook 'emmet-mode))

Blogging

To write articles on the BuildFunThings site I use org-mode. I then publish them using org2blog package.

(use-package org2blog
  :ensure t
  :config
  ;; Experiments
  (require 'auth-source) ;; or nothing if already in the load-path

  (let (credentials)
    ;; only required if your auth file is not already in the list of auth-sources
    ;; Always keep config and username/password separated
    (add-to-list 'auth-sources "~/.authinfo")
    (setq credentials (auth-source-user-and-password "buildfunthings.com"))
    (setq org2blog/wp-blog-alist
          `(("bft"
             :url "https://www.buildfunthings.com/xmlrpc.php"
             :username ,(car credentials)
             :password ,(cadr credentials))))))

Experiments

The following are snippets, functions or other temporary code that I have found or created but that do not have a solid place in my workflow yet.

;; helper functions


(defun nuke-all-buffers ()
  (interactive)
  (mapcar 'kill-buffer (buffer-list))
  (delete-other-windows))

(setq mac-right-alternate-modifier nil)

;; Customize EWW for dark background
(setq shr-color-visible-luminance-min 80)
(use-package html-to-hiccup
  :ensure t
  :config
  ;;(define-key clojure-mode-map (kbd "H-h") 'html-to-hiccup-convert-region)
  )

;; Experiments

Mode line

(use-package mode-icons
  :ensure t
  :config
  (mode-icons-mode t)
)
;;  (use-package spaceline
;;    :ensure t
;;    :init
;;    (setq powerline-default-separator 'utf-8)
;;
;;    :config
;;    (require 'spaceline-config)
;;    (spaceline-spacemacs-theme)
;;    )
;; Reference: https://github.com/hlissner/.emacs.d/blob/master/core/core-modeline.el

(use-package f
  :ensure t)

(use-package projectile
  :ensure t)

(use-package powerline
  :ensure t
  :config
  (defvar mode-line-height 30 "A little bit taller, a little bit baller.")

  (defvar mode-line-bar          (eval-when-compile (pl/percent-xpm mode-line-height 100 0 100 0 3 "#909fab" nil)))
  (defvar mode-line-eldoc-bar    (eval-when-compile (pl/percent-xpm mode-line-height 100 0 100 0 3 "#B3EF00" nil)))
  (defvar mode-line-inactive-bar (eval-when-compile (pl/percent-xpm mode-line-height 100 0 100 0 3 "#9091AB" nil)))

  ;; Custom faces
  (defface mode-line-is-modified nil
    "Face for mode-line modified symbol")

  (defface mode-line-2 nil
    "The alternate color for mode-line text.")

  (defface mode-line-highlight nil
    "Face for bright segments of the mode-line.")

  (defface mode-line-count-face nil
    "Face for anzu/evil-substitute/evil-search number-of-matches display.")

  ;; Git/VCS segment faces
  (defface mode-line-vcs-info '((t (:inherit warning)))
    "")
  (defface mode-line-vcs-warning '((t (:inherit warning)))
    "")

  ;; Flycheck segment faces
  (defface doom-flycheck-error '((t (:inherit error)))
    "Face for flycheck error feedback in the modeline.")
  (defface doom-flycheck-warning '((t (:inherit warning)))
    "Face for flycheck warning feedback in the modeline.")


  (defun doom-ml-flycheck-count (state)
    "Return flycheck information for the given error type STATE."
    (when (flycheck-has-current-errors-p state)
      (if (eq 'running flycheck-last-status-change)
          "?"
        (cdr-safe (assq state (flycheck-count-errors flycheck-current-errors))))))

  (defun doom-fix-unicode (font &rest chars)
    "Display certain unicode characters in a specific font.
e.g. (doom-fix-unicode \"DejaVu Sans\" ?⚠ ?★ ?λ)"
    (declare (indent 1))
    (mapc (lambda (x) (set-fontset-font
                  t (cons x x)
                  (cond ((fontp font)
                         font)
                        ((listp font)
                         (font-spec :family (car font) :size (nth 1 font)))
                        ((stringp font)
                         (font-spec :family font))
                        (t (error "FONT is an invalid type: %s" font)))))
          chars))

  ;; Make certain unicode glyphs bigger for the mode-line.
  ;; FIXME Replace with all-the-icons?
  (doom-fix-unicode '("DejaVu Sans Mono" 15) ?✱) ;; modified symbol
  (let ((font "DejaVu Sans Mono for Powerline"))
    (doom-fix-unicode (list font 12) ?)  ;; git symbol
    (doom-fix-unicode (list font 16) ?∄)  ;; non-existent-file symbol
    (doom-fix-unicode (list font 15) ?)) ;; read-only symbol

  ;; So the mode-line can keep track of "the current window"
  (defvar mode-line-selected-window nil)
  (defun doom|set-selected-window (&rest _)
    (let ((window (frame-selected-window)))
      (when (and (windowp window)
                 (not (minibuffer-window-active-p window)))
        (setq mode-line-selected-window window))))
  (add-hook 'window-configuration-change-hook #'doom|set-selected-window)
  (add-hook 'focus-in-hook #'doom|set-selected-window)
  (advice-add 'select-window :after 'doom|set-selected-window)
  (advice-add 'select-frame  :after 'doom|set-selected-window)

  (defun doom/project-root (&optional strict-p)
    "Get the path to the root of your project."
    (let (projectile-require-project-root strict-p)
      (projectile-project-root)))

  (defun *buffer-path ()
    "Displays the buffer's full path relative to the project root (includes the
project root). Excludes the file basename. See `*buffer-name' for that."
    (when buffer-file-name
      (propertize
       (f-dirname
        (let ((buffer-path (file-relative-name buffer-file-name (doom/project-root)))
              (max-length (truncate (/ (window-body-width) 1.75))))
          (concat (projectile-project-name) "/"
                  (if (> (length buffer-path) max-length)
                      (let ((path (reverse (split-string buffer-path "/" t)))
                            (output ""))
                        (when (and path (equal "" (car path)))
                          (setq path (cdr path)))
                        (while (and path (<= (length output) (- max-length 4)))
                          (setq output (concat (car path) "/" output))
                          (setq path (cdr path)))
                        (when path
                          (setq output (concat "../" output)))
                        (when (string-suffix-p "/" output)
                          (setq output (substring output 0 -1)))
                        output)
                    buffer-path))))
       'face (if active 'mode-line-2))))

  (defun *buffer-name ()
    "The buffer's base name or id."
    ;; FIXME Don't show uniquify tags
    (s-trim-left (format-mode-line "%b")))

  (defun *buffer-pwd ()
    "Displays `default-directory', for special buffers like the scratch buffer."
    (propertize
     (concat "[" (abbreviate-file-name default-directory) "]")
     'face 'mode-line-2))

  (defun *buffer-state ()
    "Displays symbols representing the buffer's state (non-existent/modified/read-only)"
    (when buffer-file-name
      (propertize
       (concat (if (not (file-exists-p buffer-file-name))
                   ""
                 (if (buffer-modified-p) ""))
               (if buffer-read-only ""))
       'face 'mode-line-is-modified)))

  (defun *buffer-encoding-abbrev ()
    "The line ending convention used in the buffer."
    (if (memq buffer-file-coding-system '(utf-8 utf-8-unix))
        ""
      (symbol-name buffer-file-coding-system)))

  (defun *major-mode ()
    "The major mode, including process, environment and text-scale info."
    (concat (format-mode-line mode-name)
            (if (stringp mode-line-process) mode-line-process)
            (and (featurep 'face-remap)
                 (/= text-scale-mode-amount 0)
                 (format " (%+d)" text-scale-mode-amount))))

  (defun *vc ()
    "Displays the current branch, colored based on its state."
    (when vc-mode
      (let ((backend (concat "" (substring vc-mode (+ 2 (length (symbol-name (vc-backend buffer-file-name)))))))
            (face (let ((state (vc-state buffer-file-name)))
                    (cond ((memq state '(edited added))
                           'mode-line-vcs-info)
                          ((memq state '(removed needs-merge needs-update conflict removed unregistered))
                           'mode-line-vcs-warning)))))
        (if active
            (propertize backend 'face face)
          backend))))

  (defvar-local doom--flycheck-err-cache nil "")
  (defvar-local doom--flycheck-cache nil "")
  (defun *flycheck ()
    "Persistent and cached flycheck indicators in the mode-line."
    (when (and (featurep 'flycheck)
               flycheck-mode
               (or flycheck-current-errors
                   (eq 'running flycheck-last-status-change)))
      (or (and (or (eq doom--flycheck-err-cache doom--flycheck-cache)
                   (memq flycheck-last-status-change '(running not-checked)))
               doom--flycheck-cache)
          (and (setq doom--flycheck-err-cache flycheck-current-errors)
               (setq doom--flycheck-cache
                     (let ((fe (doom-ml-flycheck-count 'error))
                           (fw (doom-ml-flycheck-count 'warning)))
                       (concat
                        (if fe (propertize (format "%d " fe)
                                           'face (if active
                                                     'doom-flycheck-error
                                                   'mode-line)))
                        (if fw (propertize (format "%d " fw)
                                           'face (if active
                                                     'doom-flycheck-warning
                                                   'mode-line))))))))))

  (defun *buffer-position ()
    "A more vim-like buffer position."
    (let ((start (window-start))
          (end (window-end))
          (pend (point-max)))
      (if (and (= start 1)
               (= end pend))
          ":All"
        (cond ((= start 1) ":Top")
              ((= end pend) ":Bot")
              (t (format ":%d%%%%" (/ end 0.01 pend)))))))

  (defun my-mode-line (&optional id)
    `(:eval
      (let* ((active (eq (selected-window) mode-line-selected-window))
             (lhs (list (propertize " " 'display (if active mode-line-bar mode-line-inactive-bar))
                        (*flycheck)
                        " "
                        (*buffer-path)
                        (*buffer-name)
                        " "
                        (*buffer-state)
                        ,(if (eq id 'scratch) '(*buffer-pwd))))
             (rhs (list (*buffer-encoding-abbrev) "  "
                        (*vc)
                        " "
                        (when persp-curr persp-modestring)
                        " " (*major-mode) "  "
                        (propertize
                         (concat "(%l,%c) " (*buffer-position))
                         'face (if active 'mode-line-2))))
             (middle (propertize
                      " " 'display `((space :align-to (- (+ right right-fringe right-margin)
                                                         ,(1+ (string-width (format-mode-line rhs)))))))))
        (list lhs middle rhs))))

  (setq-default mode-line-format (my-mode-line)))

OUTDATED Enhancements

;; (use-package ivy
;;   :ensure t
;;   :diminish ivy-mode
;;   :config
;;   (defun couns-git ()
;;     "Find file in the current Git repository."
;;     (interactive)
;;     (let* ((default-directory (locate-dominating-file
;;                                default-directory ".git"))
;;            (cands (split-string
;;                    (shell-command-to-string
;;                     "git ls-files --full-name --")
;;                    "\n"))
;;            (file (ivy-read "Find file: " cands)))
;;       (when file
;;         (find-file file))))
;;   :bind ("M-o" . couns-git)
;;   )
;; 
;; (use-package swiper
;;   :ensure t)
;; 
;; (use-package counsel
;;   :ensure t)

Email

;; (require 'mu4e)
;; 
;; (defun my-render-html-message ()
;;   (let ((dom (libxml-parse-html-region (point-min) (point-max))))
;;     (erase-buffer)
;;     (shr-insert-document dom)
;;     (goto-char (point-min))))
;; 
;; (setq mu4e-html2text-command 'my-render-html-message)
;; ;;(setq mu4e-html2text-command "html2text -utf8 -width 72")
;; 
;; ;; default
;; (setq mu4e-maildir (expand-file-name "~/Maildir"))
;; 
;; (setq mu4e-drafts-folder "/Drafts")
;; (setq mu4e-sent-folder   "/Sent Mail")
;; (setq mu4e-trash-folder  "/Trash")
;; 
;; ;; don't save message to Sent Messages, GMail/IMAP will take care of this
;; (setq mu4e-sent-messages-behavior 'delete)
;; 
;; ;; setup some handy shortcuts
;; (setq mu4e-maildir-shortcuts
;;       '(("/Personal/INBOX"             . ?i)
;;         ("/Sent Mail" . ?s)
;;         ("/Trash"     . ?t)))
;; 
;; ;; allow for updating mail using 'U' in the main view:
;; (setq mu4e-get-mail-command "offlineimap")
;; 
;; ;; something about ourselves
;; ;; I don't use a signature...
;; (setq
;;  user-mail-address "arjen@wiersma.org"
;;  user-full-name  "Arjen Wiersma"
;;  ;; message-signature
;;  ;;  (concat
;;  ;;    "Foo X. Bar\n"
;;  ;;    "http://www.example.com\n")
;;  )
;; 
;; ;; sending mail -- replace USERNAME with your gmail username
;; ;; also, make sure the gnutls command line utils are installed
;; ;; package 'gnutls-bin' in Debian/Ubuntu, 'gnutls' in Archlinux.
;; 
;; (require 'smtpmail)
;; 
;; (setq message-send-mail-function 'smtpmail-send-it
;;       starttls-use-gnutls t
;;       smtpmail-starttls-credentials
;;       '(("smtp.fastmail.com" 587 nil nil))
;;       smtpmail-auth-credentials
;;       (expand-file-name "~/.authinfo.gpg")
;;       smtpmail-default-smtp-server "smtp.fastmail.com"
;;       smtpmail-smtp-server "smtp.fastmail.com"
;;       smtpmail-smtp-service 587
;;       smtpmail-debug-info t)

Quantified Self

(setq org-capture-templates
  '(    

    ("f" "Fitness Entry"
         entry (file+datetree "~/Dropbox/org/Notes/fitness.org")
         "* %?"
         :empty-lines 1)

        
    ))