Skip to content

FrancisMurillo/.emacs.d

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Francis Murillo’s Emacs configuration

Introduction

If I Forget

No words can describe my awe with Emacs and continue to do so. A text editor with a lisp interpreter, it’s concept is so simple and sublime. What joy.

I remember starting Emacs seriously at around August of 2015. Before then, I picked it up lightly and thought(not use) about it; but what really drove me to use it is.

Growth

I was using an IDE before and somebody told me that GUIs change but the shell remains the same. I found myself over time exploring keyboard shortcuts, better terminals and Emacs(or maybe vi).

Lightweight

I had a crappy laptop that always needed to be plugged, had a lot dead pixels, and closes when it overheats; I needed something that would work with what I had.

Linux

Moving from Windows to Linux forced me to reevaluate the software I used. Adopting a new philosophy and OS pushed me in the right direction

Whatever the reason might have been. I just want to say I’m a happy Emacs user and I find comfort and joy in hearing other people talk and share about it.

About My Configuration

I use org-babel as my configuration file once I heard you can do so. Splitting the configuration to multiple files was the plan but the way it weaves nicely to documentation immediately drew me in.

You can load this with (org-babel-load-file "/path/to/file") if you need to reload any changes or just take any snippet you want. I do my best to make my configuration copy friendly on different environments.

Or with an function instead

;;; -*- lexical-binding: t -*-

(progn
  (defun fn/reload-config ()
    "Reload my configuration again"
    (interactive)
    (org-babel-load-file
     (expand-file-name fn/config-file user-emacs-directory)))

  (defun fn/dired-emacs-dir ()
    "Quickly visit emacs directory"
    (interactive)
    (dired user-emacs-directory)))

Bootstrap

Basic configuration to allow for this configuration style

Lexical Binding

Lexical scoping is required for sanity and modularity

(setq lexical-binding t)

Package Repo

Set package repositories

(defconst fn/package-archives
  `(("gnu" "http://elpa.gnu.org/packages/" 10)
    ("org" "http://orgmode.org/elpa/" 30)
    ("melpa" "http://melpa.org/packages/" 20))
  "List of my packages")

(mapc
 (lambda (package-archive)
   (pcase-let ((`(,id ,location ,priority) package-archive))
     (unless (assoc-default id package-archives)
       (add-to-list 'package-archives (cons id location))
       (add-to-list 'package-archive-priorities (cons id priority)))))
 fn/package-archives)

Package Loader

This config uses use-package to load packages lazily.

(require 'use-package)

(setq use-package-verbose t
      use-package-check-before-init t
      use-package-minimum-reported-time 0.025
      use-package-always-defer t
      use-package-always-defer-install nil)

(use-package system-packages
  :ensure t
  :config
  (setq system-packages-noconfirm t)

  (when (and (executable-find "pacman"))
    (cond
     ((executable-find "yay")
      (add-to-list 'system-packages-supported-package-managers
                   '(yay .
                         ((default-sudo . nil)
                          (install . "yay -S")
                          (search . "yay -Ss")
                          (uninstall . "yay -Rs")
                          (update . "yay -Syu")
                          (clean-cache . "yay -Sc")
                          (log . "cat /var/log/pacman.log")
                          (change-log . "yay -Qc")
                          (get-info . "yay -Qi")
                          (get-info-remote . "yay -Si")
                          (list-files-provided-by . "yay -Ql")
                          (owning-file . "yay -Qo")
                          (owning-file-remote . "yay -F")
                          (verify-all-packages . "yay -Qkk")
                          (verify-all-dependencies . "yay -Dk")
                          (remove-orphaned . "yay -Rns $(pacman -Qtdq)")
                          (list-installed-packages . "yay -Qe")
                          (list-installed-packages-all . "yay -Q")
                          (list-dependencies-of . "yay -Qi")
                          (noconfirm . "--noconfirm"))))

      (setq system-packages-package-manager 'yay
            system-packages-use-sudo nil))
     ((executable-find "pacaur") (setq system-packages-package-manager 'pacaur))
     (t nil))))

(use-package use-package-ensure-system-package
  :ensure t
  :demand t)

(defun fn/remove-byte-compiled-files ()
  "Remove byte compiles from emacs directory"
  (interactive)
  (shell-command
   (format "cd '%s' && find . -name '*.elc' -type f | xargs rm -f"
           (expand-file-name user-emacs-directory))))

(setq org-confirm-babel-evaluate nil
      org-src-fontify-natively t
      org-src-tab-acts-natively t)

Constants

Global constants important to this configuration

(defconst fn/cache-dir-name ".cache"
  "Place every moving file in this directory")

(defconst fn/cache-dir (expand-file-name fn/cache-dir-name user-emacs-directory)
  "Every cached or moving file should be here like with Spacemacs")

(make-directory fn/cache-dir t)

(defconst fn/setting-dir-name ".setting"
  "Just like `fn/cache-dir-name' but for my persistent setting.")

(defconst fn/setting-dir (expand-file-name fn/setting-dir-name user-emacs-directory)
  "Just like `fn/cache-dir' but for persistent settings.")

(make-directory fn/setting-dir t)

(defconst fn/lib-dir-name "lib"
  "External non-standard files directory")

(defconst fn/lib-dir (expand-file-name fn/lib-dir-name user-emacs-directory)
  "External library directory")

(defconst fn/system-dir-name "system"
  "System library dependencies directory")

(defconst fn/system-dir (expand-file-name fn/system-dir-name user-emacs-directory)
  "System library for external files.")

(defconst fn/conf-dir-name "conf"
  "Config dependencies directory")

(defconst fn/conf-dir (expand-file-name fn/conf-dir-name fn/system-dir)
  "External conf directory.")

(defconst fn/extra-dir-name "extra"
  "Anything under the sun you can put here")

(defconst fn/extra-dir (expand-file-name fn/extra-dir-name user-emacs-directory)
  "Anything under the sun directory")

(make-directory fn/extra-dir t)

(defconst fn/custom-module-dir-name "modules"
  "Custom elisp packages directory name")

(defconst fn/custom-module-dir (expand-file-name fn/custom-module-dir-name fn/library-dir)
  "Custom elisp packages directory")

(defconst fn/custom-script-dir-name "scripts"
  "Custom elisp script directory name")

(defconst fn/custom-script-dir (expand-file-name fn/custom-script-dir-name fn/library-dir)
  "Custom elisp script directory")

(add-to-list 'load-path fn/custom-script-dir)

Namespace

(defgroup fn nil
  "My namespace for customizing my configuration")

;;* Custom Prefixes
;; Anything that just calls normal commands
;; Binding: C-c n
(define-prefix-command 'fn-standard-prefix-map)

;; Anything that I worked experimentally on
;; Binding: C-c m
(define-prefix-command 'fn-custom-prefix-map)

;; Anything that is important while I am working on something
;; Binding: C-c C-m / C-c C-n / C-c b / C-C C-b
(define-prefix-command 'fn-work-prefix-map)

(defun fn/make-prefixed-keymap (key &optional base-keymap)
  "Make a sparse keymap that is already prefixed by KEY.
It also accepts a BASE-KEYMAP if you are prefixing an existing key map."
  (let* ((prefixed-keymap (make-sparse-keymap))
         (target-keymap (or base-keymap
                            prefixed-keymap)))
    (define-key target-keymap key prefixed-keymap)
    prefixed-keymap))

;;* Custom Key Sequences
(defconst fn/standard-key-sequence (kbd "C-c n")
  "My standard key sequence.")

(defconst fn/custom-key-sequence (kbd "C-c m")
  "My custom key sequence.")

(defconst fn/work-key-sequence (kbd "C-c C-m")
  "My work key sequence.")

(defun fn/make-work-keymap (&optional base-keymap)
  "Just `fn/make-prefixed-keymap` with `fn/work-key-sequence`."
  (fn/make-prefixed-keymap fn/work-key-sequence base-keymap))

(global-set-key (kbd "C-c n") fn-standard-prefix-map)
(global-set-key (kbd "C-c m") fn-custom-prefix-map)

Vanilla

Configuration that applies for vanilla Emacs or no packages

;; No need for startup screen
(setq inhibit-startup-screen t
      initial-scratch-message nil)

(setq max-specpdl-size 10000
      max-lisp-eval-depth 5000)

;; Ignore customization via `customize-group'
(setq custom-file (expand-file-name "custom-file.el" fn/cache-dir))

;; UTF-8 should be the enivorment
(set-language-environment 'utf-8)
(setq locale-coding-system 'utf-8)
(set-default-coding-systems 'utf-8)
(set-terminal-coding-system 'utf-8)
(unless (eq system-type 'windows-nt)
  (set-selection-coding-system 'utf-8))
(prefer-coding-system 'utf-8)
(setq buffer-file-coding-system 'utf-8)

;; Visual bell for me since audio is a bit more distractive
(setq visible-bell t)

(fset 'yes-or-no-p 'y-or-n-p) ;; Y or N

(mouse-avoidance-mode 'cat-and-mouse)
(setq comint-input-ignoredups t)


(put 'narrow-to-region 'disabled nil)
(put 'downcase-region 'disabled nil)
(put 'upcase-region 'disabled nil)

(setq enable-local-variables :safe)

(add-to-list 'safe-local-variable-values '(major-mode))
(add-to-list 'safe-local-variable-values '(auto-save-default))
(add-to-list 'safe-local-variable-values  '(backup-inhibited . t))
(add-to-list 'safe-local-variable-values '(epa-file-encrypt-to))

(setq sentence-end-double-space nil
      require-final-newline t)

;; No line too long font locking please
(setq whitespace-line-column 10000)

;; Use tabs as spaces
(setq-default indent-tabs-mode nil)

(add-hook 'makefile-mode 'indent-tabs-mode)

(tooltip-mode -1)
(tool-bar-mode -1)
(menu-bar-mode -1)
(fringe-mode 0)

(add-hook 'after-init-hook 'global-whitespace-mode)
(diminish 'global-whitespace-mode)

(global-hl-line-mode t)
(diminish 'hl-line-mode)


(global-font-lock-mode t)

(setq font-lock-support-mode 'jit-lock-mode)
(setq font-lock-maximum-decoration t)

(show-paren-mode t)

(setq show-paren-style 'expression)

Basic

Basic configuration without libraries

(fset 'fn/add-major-mode-icon #'ignore)

Mode Line

My mode line customization.

(defun fn/load-modeline ()
  "Load custom modeline via `moder''"
  (interactive)
  (require 'moder))

(unless noninteractive
  (add-hook 'after-init-hook #'fn/load-modeline))

Theme

Use tronesque theme

(defconst fn/tronsesque-package-dir (expand-file-name "tronesque/" fn/custom-module-dir)
  "Tronesque package dir")

(add-to-list 'load-path fn/tronsesque-package-dir)
(when  (require 'tronesque-theme)
  (load-theme 'tronesque t))

Dired

Directory view

(setq dired-dwim-target t
      dired-listing-switches "-alh"
      dired-recursive-copies 'always
      dired-recursive-deletes 'always
      dired-isearch-filenames t
      dired-copy-preserve-time t)

(with-eval-after-load 'async
  (dired-async-mode t))

(add-hook 'dired-mode-hook 'dired-hide-details-mode)

(require 'dired-x)

(with-eval-after-load 'all-the-icons
  (fn/add-major-mode-icon
   'wdired-mode
   '(all-the-icons-octicon "diff" :v-adjust 0.0)))

TRAMP

TRAMP is too good to pass up

(setq tramp-persistency-file-name (expand-file-name "tramp" fn/cache-dir)
      tramp-default-user (getenv "USER")
      tramp-verbose 6
      tramp-ssh-controlmaster-options
      "-o ControlMaster=auto -o ControlPath='tramp.%%C' -o ControlPersist=no")

Backup & Auto-save

(defconst fn/backup-dir (expand-file-name "backups" fn/cache-dir)
  "Backup directory")

(defconst fn/auto-save-dir (expand-file-name "auto-save-list" fn/cache-dir)
  "Auto save directory")

(setq auto-save-timeout 15
      auto-save-list-file-name fn/auto-save-dir
      delete-old-versions t
      version-control t
      vc-make-backup-files t
      backup-by-copying t
      kept-new-versions 10
      kept-old-versions 50
      backup-directory-alist `(("." . ,fn/backup-dir))
      auto-save-list-file-prefix fn/auto-save-dir
      auto-save-file-name-transforms `((".*" ,fn/auto-save-dir t)))

Font

Use Source Code Pro Regular and Courier as default

(defconst fn/primary-font-name "Source Code Pro"
  "Primary font.")

(defconst fn/secondary-font-name "DejaVu Sans Mono"
  "Secondary font.")

(defconst fn/font-size 10
  "Font size")

(defconst fn/font-dir-name "fonts"
  "Font dependencies directory")

(defconst fn/font-dir (expand-file-name fn/font-dir-name fn/system-dir)
  "External font directory.")

(defun fn/install-fonts ()
  "Install preferred fonts in the `~/.local/share/fonts' folder.
Tested on Arch Linux."
  (interactive)
  (let ((local-font-folder (expand-file-name "~/.local/share/fonts/")))
    (make-directory local-font-folder t)
    (mapc
     (lambda (font-file)
       (copy-file font-file local-font-folder t))
     (directory-files fn/font-dir  t ".ttf"))

    (when (executable-find "fc-cache")
      (shell-command "fc-cache -f"))))

(let (font-list (font-family-list))
  (when (and (eq system-type 'gnu/linux)
             (or (eq (member fn/primary-font-name (font-family-list)) nil)
                 (eq (member fn/secondary-font-name (font-family-list)) nil))
             (yes-or-no-p "It seems one of the nice fonts you want are missing. Try to install it with `fn/install-fonts'?"))
    (fn/install-fonts))
  (cond
   ((member fn/primary-font-name font-list)
    (set-frame-font (format "%s %s" fn/primary-font-name fn/font-size) t t))
   ((member fn/secondary-font-name font-list)
    (set-frame-font (format "%s %s" fn/secondary-font-name fn/font-size) t t))
   (t nil)))

Garbage Collection

Set a high GC

(setq gc-cons-threshold (* 256 1024 1024))
(run-with-idle-timer 30 t #'garbage-collect)

Custom GC Message

I love the saying in Continue9876543210 and want to incorporate that.

(defvar fn/my-lightning (concat
                         (propertize
                          " MY LIGHTNING "
                          'face '(:background "#ff1e02" :foreground "#110200"
                                              :box (:line-width 0 :color "#ff1e02")))
                         "...")
  "A reference to continue?987654321.
May I accept the lightning.")

(defvar fn/my-prayer (format "%s %s"
                             fn/my-lightning
                             (concat
                              (propertize
                               " MY PRAYER "
                               'face '(:background "#ffffff" :foreground "#121212"
                                                   :box (:line-width 0 :color "#ffffff")))
                              "..."))
  "The closing message for garbage collection.
May I be lift up.")

(setq garbage-collection-messages nil) ;; My lightning... my prayer

(defun fn/continue-gc (orig-gc &rest args)
  "Wrap some message with ORIG-GC."
  (let ((start-time (current-time)))
    (if (or (minibuffer-prompt) (current-message))
        (apply orig-gc args)
      (prog2
          (message fn/my-lightning)
          (apply orig-gc args)
        (message "%s %s"
                 fn/my-prayer
                 (format "(%.4fs)"
                         (float-time (time-subtract (current-time) start-time))))))))

(advice-add 'garbage-collect :around #'fn/continue-gc)

Bookmark

Set bookmark to cache

(setq bookmark-file (expand-file-name "bookmarks" fn/cache-dir)
      bookmark-default-file (expand-file-name "bookmarks-default" fn/cache-dir))

(when (require'rx)
  (add-to-list
   'display-buffer-alist
   (cons
    (rx bos "*Bookmark List*" eos)
    (cons 'display-buffer-same-window (list)))))

Editing

Some editing configurations

History

Save everything

(setq history-length t
      history-delete-duplicates t)

ISearch

Incremental search that skips whitespace is good.

(setq search-highlight t)

;; This ignores whitespace when searching
(setq-default search-whitespace-regexp ".*?")

Coding

Some programming configuration that make sense

(setq save-interprogram-paste-before-kill t)

;; Please indent after newline to maintain sanity
(global-set-key (kbd "RET") 'newline-and-indent)

Compilation

Compilation config

(setq compilation-window-height 10
      compilation-scroll-output 'first-error
      compilation-ask-about-save nil)

(when (boundp 'comp-eln-load-path)
  (add-to-list 'comp-eln-load-path (expand-file-name ".eln-cache" fn/cache-dir)))

Auth Sources

Auth source configuration

(setq auth-sources (list (list  :source "~/.authinfo.gpg")))

Browser

Browser config which defaults to firefox

(setq browse-url-browser-function 'browse-url-generic
      browse-url-generic-program "firefox-esr")

Modes

Builtin library configurations

bookmark

Bookmark configuration

(when (require 'bookmark)
  (setq bookmark-default-file (expand-file-name "bookmarks" fn/cache-dir)
        bookmark-save-flag t))

savehist

Save minibuffer history

(when (require 'savehist)
  (defconst fn/savehist-file (expand-file-name "savehist" fn/cache-dir)
    "Save history file")

  (setq savehist-file fn/savehist-file
        savehist-save-minibuffer-history t
        savehist-additional-variables (list 'kill-ring
                                            'search-ring
                                            'regexp-search-ring))
  (savehist-mode t))

visual-line

Visual lines make more sense than the hard lines, what you see is what you get. Besides, I use syntax motions

(global-visual-line-mode t)
(diminish 'visual-line-mode)

auto-fill

Useful mode when writing, keeps things under 80 characters.

(add-hook 'text-mode-hook 'turn-on-auto-fill)
(diminish 'auto-fill-function)
(setq-default fill-column 72)

auto-revert

The more generic revert

(global-auto-revert-mode t)

(setq global-auto-revert-non-file-buffers t
      auto-revert-verbose nil)

(diminish 'auto-revert-mode)

autorevert

Enhancement for log reading

(use-package autorevert
  :diminish auto-revert-mode
  :mode (("\\.log\\'" . auto-revert-tail-mode)));

url

Set url cache

(with-eval-after-load 'url
  (setq url-configuration-directory
        (expand-file-name "url" fn/cache-dir)))

Dependencies

Everything needed to support this more complex configurations

(use-package dash
  :ensure t)

(use-package dash-functional
  :ensure t)

(use-package async
  :ensure t)

(use-package s
  :ensure t)

(use-package f
  :ensure t)

exec-path-from-shell

The path variable from the shell is need to run commands

(use-package exec-path-from-shell
  :ensure t
  :demand t
  :if (not (eq system-type 'windows-nt)) ;; Sorry Windows
  :config
  (setq exec-path-from-shell-variables (list "PATH" "MANPATH" "PROMPT" "PS1")
        exec-path-from-shell-arguments (list "-l" "-i"))

  (exec-path-from-shell-initialize))

auto-compile

Automatic compilation of package files

(use-package auto-compile
  :ensure t
  :hook (emacs-lisp-mode . auto-compile-mode)
  :config
  (auto-compile-on-load-mode)
  (auto-compile-on-save-mode))

all-the-icons

Making Emacs more modern.

(use-package all-the-icons
  :ensure t
  :demand t
  :unless noninteractive
  :init
  (fset 'fn/add-major-mode-icon #'ignore)
  :config
  (defun fn/add-major-mode-icon (main-mode icon-config)
    "Add icon mapping to major mode given MAIN-MODE, ICON-CONFIG."
    (add-to-list
     'all-the-icons-mode-icon-alist
     (append (list main-mode) icon-config)))

  (fn/add-major-mode-icon
   'makefile-mode
   (list 'all-the-icons-faicon "wrench" :v-adjust -0.1))

  (fn/add-major-mode-icon
   'special-mode
   (list 'all-the-icons-faicon "birthday-cake" :v-adjust -0.1))

  (fn/add-major-mode-icon
   'fundamental-mode
   (list 'all-the-icons-faicon "pencil" :v-adjust -0.1))

  (fn/add-major-mode-icon
   'compilation-mode
   (list 'all-the-icons-faicon "cog" :v-adjust -0.1))

  (fn/add-major-mode-icon
   'process-menu-mode
   (list 'all-the-icons-faicon "list" :v-adjust -0.1))

  (fn/add-major-mode-icon
   'package-menu-mode
   (list 'all-the-icons-octicon "package" :v-adjust 0.0))

  (fn/add-major-mode-icon
   'outline-mode
   (list 'all-the-icons-faicon "list-ul" :v-adjust -0.1))

  (fn/add-major-mode-icon
   'conf-unix-mode
   (list 'all-the-icons-faicon "code" :v-adjust -0.1))

  (fn/add-major-mode-icon
   'edmacro-mode
   (list 'all-the-icons-faicon "pause-circle" :v-adjust -0.1)))

Assumptions

These are assumptions I have of my setup externally. After this section, everything is free.

Checking for missing executables.

(defvar fn/missing-executables (list)
  "Executables that could not be found.")

(defmacro fn/use-executables (name executables &rest body)
  "Like with `use-package' but used in checking for executables."
  `(progn
       (message "Checking executable set %s" ,(symbol-name name))
       (if (not (and ,@(mapcar
                    (lambda (executable)
                      `(executable-find ,(symbol-name executable)) )
                    executables)))
           (message "Missing executable set %s" ,(symbol-name name))
         ,@body)
       (quote ,executables)))

(put 'fn/use-executables 'lisp-indent-function 'defun)

Terminator

Shell related configuration

shell

Some configurations on the terminal

(setq async-shell-command-buffer 'new-buffer)

(with-eval-after-load 'all-the-icons
  (fn/add-major-mode-icon
   'shell-mode
   (list 'all-the-icons-faicon "terminal" :v-adjust 0.0)))

term

An enhancement for term

(use-package term
  :bind (:map fn-standard-prefix-map
              ("x t" . ansi-term))
  :config
  (defadvice term-sentinel (around ansi-term-kill-buffer (proc msg))
    (if (memq (process-status proc) '(signal exit))
        (let ((buffer (process-buffer proc)))
          ad-do-it
          (kill-buffer buffer))
      ad-do-it))
  (ad-activate 'term-sentinel)

  ;; Set the term program and ask for a name
  (defadvice ansi-term (before ansi-term-force-shell)
    (interactive (list (getenv "SHELL")
          (let ((term-name (string-trim (read-from-minibuffer "Name the term buffer: "))))
            (if (string-empty-p term-name) nil term-name)))))
  (ad-activate 'ansi-term)


  (add-hook 'term-mode-hook 'goto-address-mode)
  (add-hook 'term-exec-hook
            #'(lambda ()
               (set-buffer-process-coding-system 'utf-8-unix 'utf-8-unix))))

eshell

Another enhancement for the shell

(use-package eshell
  :bind (:map fn-standard-prefix-map
              ("x e" . eshell))
  :config
  (defun fn/eshell-prompt-function ()
    "My eshell prompt function."
    (concat " λ "))

  (setq eshell-highlight-prompt nil
     eshell-hist-ignoredups t
     eshell-directory-name (expand-file-name "eshell" fn/cache-dir)
     eshell-prefer-lisp-functions t
     eshell-prompt-function #'fn/eshell-prompt-function))

Autocompletion

We got completion here

(use-package pcomplete
  :ensure t
  :after eshell)

(use-package pcmpl-git
  :ensure t
  :after pcomplete)

(use-package pcmpl-pip
  :ensure t
  :after pcomplete)

(use-package pcomplete-extension
  :ensure t
  :after pcomplete)

direnv

Env per directory. (Want to hack this with Emacs)

(use-package direnv
  :ensure t
  :ensure-system-package direnv
  :demand t
  :if (not noninteractive)
  :config
  (direnv-mode))

prodigy

Living in the shell requires some genius

(defconst fn/prodigy-map (fn/make-prefixed-keymap (kbd "P") fn-standard-prefix-map)
  "My custom prodigy map.")

(defconst fn/prodigy-command-dir (expand-file-name "prodigy-command/" fn/custom-module-dir)
  "My custom command dir.")

(add-to-list 'exec-path fn/prodigy-command-dir)

(use-package prodigy
  :ensure t
  :bind (:map fn/prodigy-map
              ("P" . prodigy))
  :commands (fn/prodigy-define-service)
  :config
  ;; Alias this command so that it can be adviced
  (fset 'fn/prodigy-define-service 'prodigy-define-service)

  ;; Display prodigy buffers on the same windowx
  (add-to-list
   'display-buffer-alist
   (cons
    (rx bos "*prodigy*" eos)
    (cons 'display-buffer-same-window (list))))

  (add-to-list
   'display-buffer-alist
   (cons
    (rx bos "*prodigy-" (* anything))
    (cons 'display-buffer-same-window (list))))

  ;; Service restart shiv
  (defun prodigy-find-service-by-buffer (&optional buffer)
    "Find `prodigy' service based on BUFFER name."
    (let* ((this-prodigy-buffer-name (buffer-name buffer))
           (service (-find
                     (lambda (service)
                       (string= (prodigy-buffer-name service)
                                this-prodigy-buffer-name))
                     (prodigy-services))))
      service))

  (defun prodigy-restart-view ()
    "Restart a `prodigy-view-mode' buffer."
    (interactive)
    (-if-let (service (prodigy-find-service-by-buffer))
        (prodigy-with-refresh
         (prodigy-restart-service service))
      (error "Current buffer is not a prodigy view buffer.")))

  (define-key prodigy-view-mode-map (kbd "C-c r") #'prodigy-restart-view)

  (with-eval-after-load 'all-the-icons
    (fn/add-major-mode-icon
     'prodigy-mode
     (list 'all-the-icons-faicon "cogs" :v-adjust -0.1))

    (fn/add-major-mode-icon
     'prodigy-view-mode
     (list 'all-the-icons-faicon "cog" :v-adjust -0.1))))

Prodigy Cleanup

Ensure when Emacs closes, kill all processes

(with-eval-after-load 'prodigy
  (defvar fn/prodigy-processes (list)
    "List of processes to kill when closing.")

  (defun fn/prodigy-kill-processes ()
    "Kill all processes started by `prodigy'"
    (interactive)
    (mapc
     (lambda (process)
       (when (process-live-p process)
         (kill-process process)))
     fn/prodigy-processes)
    (prog1
        fn/prodigy-processes
      (setq fn/prodigy-processes nil)))

  (defun fn/prodigy-add-to-processes (service &rest _args)
    "Add SERVICE process to `fn/prodigy-processes'."
    (setq fn/prodigy-processes (cl-remove-if-not #'process-live-p fn/prodigy-processes))
    (let ((process (plist-get service :process)))
      (when (process-live-p process)
        (add-to-list 'fn/prodigy-processes process))))

  (advice-add 'prodigy-start-service :after #'fn/prodigy-add-to-processes)

  (add-hook 'kill-emacs-hook #'fn/prodigy-kill-processes))

Customized Prodigy

A dependency to customize prodigy, giving the command fn/prodigy-define-service.

(with-eval-after-load 'prodigy
  (defmacro fn/prodigy-with-buffer (&rest body)
    "Execute BODY within an exisiting prodigy buffer."
    `(progn
       (when (prodigy-buffer)
         (with-current-buffer (prodigy-buffer)
           ,@body))))

  (defun fn/prodigy-refresh ()
    "Refresh prodigy buffer."
    (ignore-errors
      (fn/prodigy-with-buffer
       (prodigy-refresh)
       (prodigy-goto-first-line))))


  (defun fn/prodigy-switch-to-process-buffer (service)
    "Just an wrapper for said function with SERVICE. If there is a prefix argument, it will stop it instead."
    (if current-prefix-arg
        (if (prodigy-service-stopping-p service)
            (message "Service already stopping")
          (message "Stopping %s service." (plist-get service :name))
          (prodigy-stop-service service t))
      (if (prodigy-service-started-p service)
          (prodigy-switch-to-process-buffer service)
        (message "Starting %s service." (plist-get service :name))
        (prodigy-start-service service (apply-partially #'prodigy-switch-to-process-buffer service)))))


  (defun fn/prodigy-prepared-switch-to-process-buffer (service)
    "Another wrapper to make specific functions for viewing SERVICE."
    (let* ((service-name (plist-get service :name))
           (command-name (or (plist-get service :bind-command-name)
                             (symbol-name (gensym "prodigy-view-"))))
           (prefix "fmpv/")
           (function-symbol (intern (format "%s%s" prefix command-name)))
           (service service))
      (fset function-symbol
            `(lambda ()
               ,(format "A prodigy view function for %s" service-name)
               (interactive)
               (fn/prodigy-switch-to-process-buffer (quote ,service))))
      function-symbol))

  (defun fn/prodigy-define-service-binder (orig-fun &rest args)
    "When creating a service, check for a `:bind' keyword to
create an automatic keybinding for it."
    (let ((result (apply orig-fun args)))
      (prog1
          result
        (let* ((service (car result))
               (name (plist-get service :name))
               (bind (plist-get service :bind))
               (bind-map (or (plist-get service :bind-map) global-map)))
          (when bind
            (message "Creating binding for %s" name)
            (define-key bind-map bind (fn/prodigy-prepared-switch-to-process-buffer service)))))))

  (advice-add 'fn/prodigy-define-service :around #'fn/prodigy-define-service-binder)


  ;; :on-stop and :on-start property
  (defun fn/prodigy-stop-service (service &rest args)
    "Like `prodigy-stop-service' but also checks for
     `:on-stop' which runs before it."
    (let ((on-stop (prodigy-service-or-first-tag-with service :on-stop)))
      (when (functionp on-stop)
        (apply on-stop (list :service service)))))

  (defun fn/prodigy-start-service (service &rest args)
    "Like `prodigy-start-service' but also checks for
     `:on-start' which runs after it."
    (let ((on-start (prodigy-service-or-first-tag-with service :on-start)))
      (when (functionp on-start)
        (apply on-start (list :service service)))))

  (defun fn/prodigy-define-service-on-start-and-stopper (orig-fun &rest args)
    "When creating a service, check and persist `:on-stop' and
    `:on-start' property."
    (let ((result (apply orig-fun args)))
      (prog1
          result
        (let* ((service (car result))
                       (on-stop (plist-get args :on-stop))
                       (on-start (plist-get args :on-start)))
          (when (functionp on-stop)
            (nconc service (list :on-stop on-stop)))
          (when (functionp on-start)
            (nconc service (list :on-start on-start)))))))

  (advice-add 'prodigy-stop-service :after #'fn/prodigy-stop-service)
  (advice-add 'prodigy-start-service :after #'fn/prodigy-start-service))

Service Tags

Some commands that can be refactored out

(with-eval-after-load 'prodigy
  (when (executable-find "mpd")
    (prodigy-define-tag
      :name 'mpd
      :cwd user-emacs-directory
      :command "mpd"
      :stop-signal 'kill
      :args `("--no-daemon")
      :kill-process-buffer-on-stop t)

    (defun fn/prodigy-create-mpd-conf (file options)
      "Update FILE with mpd OPTIONS."
      (with-temp-file file
        (insert
         (string-join
          (mapcar
           (lambda (pair)
             (pcase-let ((`(,key . ,value) pair))
               (format
                "%s %s"
                key
                (if (listp value)
                    (format
                     "{\n%s\n}"
                     (string-join
                      (mapcar
                       (lambda (pair)
                         (pcase-let ((`(,key . ,value) pair))
                           (format
                            "%s %s"
                            key
                            (format "\"%s\"" value))))
                       value)
                      "\n"))
                  (format "\"%s\"" value)))))
           options)
          "\n"))))))

docker

Containers eh?

(use-package docker
  :ensure t
  :ensure-system-package docker
  :if (not noninteractive)
  :commands (docker-images docker-containers docker-volumes docker-networks docker-machines)
  :config
  nil)

(use-package dockerfile-mode
  :ensure t
  :mode (("Dockerfile\\'" . dockerfile-mode)))

vagrant

Vagrant tools

(use-package vagrant
  :ensure t
  :ensure-system-package vagrant
  :if (not noninteractive)
  :config
  nil)

ansible

Ansible support if possible

(use-package ansible
  :ensure t
  :ensure-system-package ansible
  :if (not noninteractive)
  :config
  nil)

(use-package ansible-vault
  :ensure t
  :after ansible
  :config
  nil)

nix

For the nix package manager

(use-package nix-mode
  :ensure t
  :ensure-system-package nix
  :if (not noninteractive)
  :mode "\\.nix\\'"
  :config
  (require 'woman nil t))

memory-usage

Nice to know if my baby is getting to fat to quick

(use-package memory-usage
  :ensure t
  :bind (:map fn-standard-prefix-map
              ("m" . memory-usage)))

Editor

Anything to do with editing in Emacs

expand-region

Another great feature for marking

(use-package expand-region
  :ensure t
  :bind (("C-=" . er/expand-region)))

Editing

hungry-delete

Very useful default delete functionality

(use-package hungry-delete
  :ensure t
  :diminish hungry-delete-mode
  :bind (:map fn-standard-prefix-map
              ("C-d C-d" . hungry-delete-forward)
              ("C-d DEL" . hungry-delete-backward)))

undo-tree

Visualizing undo like vi

(use-package undo-tree
  :ensure t
  :defer t
  :diminish undo-tree-mode
  :bind (:map fn-standard-prefix-map
              ("C-_" . undo-tree-visualize)))

Completion

hippie-exp

Hippie expand rocks

(use-package hippie-exp
  :bind (("M-/" . hippie-expand))
  :config
  (setq hippie-expand-try-functions-list
     '(
          try-expand-dabbrev
          try-expand-dabbrev-all-buffers
          try-complete-file-name-partially
          try-complete-file-name
          try-expand-all-abbrevs
          try-expand-list
          try-expand-line
          try-complete-lisp-symbol-partially
          try-complete-lisp-symbol))

  (with-eval-after-load ggtags
    (add-to-list 'hippie-expand-try-functions-list 'ggtags-try-complete-tag)))

company

A replacement for autocomplete.

(use-package company
  :ensure
  :unless noninteractive
  :demand t
  :diminish company-mode
  :defines company-backends
  :init
  (add-hook 'prog-mode-hook 'company-mode)
  :config
  (global-set-key (kbd "TAB") #'company-indent-or-complete-common)

  (setq company-idle-delay (/ 60.0)
        company-minimum-prefix-length 2
        company-begin-commands (list 'self-insert-command))

  (setq company-show-numbers t
        company-tooltip-limit 20
        company-tooltip-align-annotations t)

  (setq company-dabbrev-time-limit 0.001
        company-dabbrev-code-time-limit 0.001
        company-dabbrev-downcase nil)

  (setq company-backends (list))

  (add-to-list 'company-backends 'company-capf)
  (add-to-list 'company-backends 'company-dabbrev)
  (add-to-list 'company-backends 'company-elisp)

  (defun fn/combine-backends (backend new-backend)
    "Combine BACKEND with NEW-BACKEND."
    (if (and (listp backend) (member new-backend backend))
        backend
      (let* ((list-backend (if (consp backend)
                               backend
                             (list backend)))
             (with-backend (if (member ':with list-backend)
                               list-backend
                             (append list-backend '(:with)))))
        (append with-backend (list new-backend)))))

  (defun fn/append-to-backends (new-backend)
    "Append NEW-BACKEND to company."
    (setq company-backends
          (mapcar
           (lambda (backend)
             (fn/combine-backends backend new-backend))
           company-backends)))

    (setq company-show-numbers t
          company-tooltip-limit 20
          company-tooltip-align-annotations t)

    (setq company-dabbrev-time-limit 0.001
          company-dabbrev-code-time-limit 0.001
          company-dabbrev-downcase nil)


    (setq company-backends (list))

    (add-to-list 'company-backends 'company-capf)
    (add-to-list 'company-backends 'company-dabbrev)
    (add-to-list 'company-backends 'company-elisp)

    (defun fn/combine-backends (backend new-backend)
      "Combine BACKEND with NEW-BACKEND."
      (if (and (listp backend) (member new-backend backend))
          backend
        (let* ((list-backend (if (consp backend)
                                 backend
                               (list backend)))
               (with-backend (if (member ':with list-backend)
                                 list-backend
                               (append list-backend '(:with)))))
          (append with-backend (list new-backend)))))

    (defun fn/append-to-backends (new-backend)
      "Append NEW-BACKEND to company."
      (setq company-backends
            (mapcar
             (lambda (backend)
               (fn/combine-backends backend new-backend))
             company-backends)))

    (setq fn/company-prog-backends (list 'company-dabbrev-code))

    (defun fn/backend-with-prog ()
      (fn/append-to-backends 'company-keywords)))

Ergonomic

Things to assist in managing Emacs

Jump

Things to jump around with

recentf

Accessing the files recently

(unless noninteractive
  (use-package recentf
    :bind (("C-x C-r" . recentf-open-files))
    :config
    (setq recentf-save-file (expand-file-name "recentf" fn/cache-dir)

       recentf-max-menu-items 1000
       recentf-max-saved-items 1000

       recentf-exclude '("TAGS" ".*-autoloads\\.el\\'"))

    (recentf-mode t))

  (use-package recentf-ext
    :ensure t
    :after recentf))

bookmark+

Enhancement for normal bookmarking

(defconst fn/bookmark+-module-dir (expand-file-name "bookmark+/" fn/custom-module-dir)
  "Archived `bookmark+' packages.")

(unless noninteractive
  (use-package bookmark+
    :load-path fn/bookmark+-module-dir
    :defer t
    ;; :commands ()
    :config
    nil))

Windows

Anything related to window management

winner

You got to have those window configuration

(use-package winner
  :ensure t
  :unless noninteractive
  :config
  (winner-mode t))

window-numbering

A must to navigate through windows with numbers.

(use-package window-numbering
  :ensure t
  :unless noninteractive
  :demand t
  :config
  (window-numbering-mode t)

  (global-set-key (kbd "M-`") #'other-frame)

  (when (require 'dash)
    (defun fn/get-window-by-number (window-number)
      "Get window by WINDOW-NUMBER."
      (-find
       (lambda (window)
         (= (window-numbering-get-number window) window-number))
       (window-list)))

    (defun fn/swap-windows (this-window that-window)
      "Swap THIS-WINDOW and THAT-WINDOW"
      (let ((this-buffer (window-buffer this-window))
            (that-buffer (window-buffer that-window)))
        (unless (eq this-buffer that-buffer)
          (set-window-buffer this-window that-buffer)
          (set-window-buffer that-window this-buffer))))

    (defun fn/swap-with-numbered-window (window-number)
      "Swap with current window with numbered window."
      (interactive "NWhat window number? ")
      (let ((this-window (selected-window))
            (that-window (fn/get-window-by-number window-number)))
        (if (null that-window)
            (message "Window %s does not exist"
                     window-number)
          (fn/swap-windows this-window that-window)
          (select-window-by-number window-number))))))

window-layout

Making window layouts

(use-package window-layout
  :ensure t
  :config
  (with-eval-after-load 'window-numbering
    (require 'dash)

    (defvar fn/current-window-layout nil
      "The current window layout.")

    (defun fn/window-numbering-assign-func ()
      "Window number assignment based on the current window layout."
      (ignore-errors
        (let ((this-window (selected-window)))
          (when (wlf:wset-p fn/current-window-layout)
            (let ((layout-index
                 (-find-index
                  (lambda (winfo) (eq (wlf:window-window winfo) this-window))
                  (wlf:wset-winfo-list fn/current-window-layout))))
              (if layout-index
                  layout-index
                nil))))))

    (setq window-numbering-assign-func 'fn/window-numbering-assign-func)))

golden-ratio

Makes windows large enough to see.

(use-package golden-ratio
  :ensure t
  :demand t
  :unless noninteractive
  :diminish golden-ratio-mode
  :bind (("C-c q" . golden-ratio)
         :map fn-standard-prefix-map
         ("q" . golden-ratio-mode))
  :config
  (golden-ratio-mode t)

  (run-with-idle-timer 3 nil #'golden-ratio-mode)

  (setq split-width-threshold nil
        golden-ratio-adjust-factor 1.0)

  ;; Frame entry exit fix
  (add-hook 'focus-in-hook #'golden-ratio)
  (add-hook 'focus-out-hook #'golden-ratio)

  (with-eval-after-load 'workgroups2
    ;; When switching workgroups, make sure the screen is rationed correctly
    (add-hook 'wg-after-switch-to-workgroup-hook #'golden-ratio))

  (with-eval-after-load 'window-numbering
    (defun fn/golden-ratio-after-select-window-by-number (&rest args)
      (golden-ratio))

    (advice-add
     #'select-window-by-number
     :after
     #'fn/golden-ratio-after-select-window-by-number)))

uniquify

Nicer naming convention

(use-package uniquify
    :if (and (version<= "24.3.1" emacs-version)
             (not noninteractive))
    :demand t
    :config
    (setq uniquify-buffer-name-style 'post-forward-angle-brackets))

Interface

Anything related to buffers

projectile

Must have a project finder when using a project.

(use-package projectile
  :ensure t
  :unless noninteractive
  :diminish projectile-mode
  :bind (("C-c p p" . projectile-switch-project)
         ("C-c p f" . projectile-find-file)
         ("C-c p d" . projectile-find-dir))
  :init
  (setq projectile-cache-file (expand-file-name "projectile.cache" fn/cache-dir)
        projectile-known-projects-file (expand-file-name "projectile-bookmarks.eld" fn/cache-dir)
        projectile-keymap-prefix (kbd "C-c p"))
  :config
  (require 'helm nil t)

  (defun fn/find-project-root ()
    "Visit project root."
    (interactive)
    (dired (projectile-project-root)))

  (define-key projectile-command-map (kbd "C-r") #'fn/find-project-root)

  (setq projectile-switch-project-action #'fn/find-project-root)

  (projectile-mode)
  (setq projectile-indexing-method 'native
        projectile-enable-caching t
        projectile-enable-idle-timer nil)

  (setq projectile-sort-order 'modification-time)

  (add-to-list 'projectile-project-root-files "config.xml"))

Per-Project Config

Use projectile to load per project config via .project.el and .project-locals.el

(with-eval-after-load 'projectile
  (defconst fn/project-file ".project.el"
    "Project configuration file")

  (defconst fn/project-local-file ".project-locals.el"
    "Project local setting file")

  (defconst fn/project-init-files `(,fn/project-file ,fn/project-local-file)
    "Project init files")

  (defun fn/load-project-file ()
    "Loads the `fn/project-file' for a project.
  This is run once after the project is loaded signifying project setup."
    (interactive)
    (when (projectile-project-p)
      (let* ((current-project-root (projectile-project-root))
             (project-init-file (expand-file-name fn/project-file current-project-root)))
        (when (file-exists-p project-init-file)
          (message "Loading project init file for %s" (projectile-project-name))
          (condition-case ex
              (load project-init-file t)
            ('error
             (message
              "There was an error loading %s: %s"
              project-init-file
              (error-message-string ex))))))))

  (defun fn/load-project-local-file ()
    "Loads the `fn/project-local-file' for a project.
  This is run for every time a file in a project is opened signifying per-file setup."
    (interactive)
    (when (projectile-project-p)
      (let* ((current-project-root (projectile-project-root))
             (project-local-init-file (expand-file-name fn/project-local-file current-project-root)))
        (when (and (file-exists-p project-local-init-file)
                   (not (member (buffer-file-name) fn/project-init-files)))
          (message
           "Loading project local file for %s on %s"
           (projectile-project-name)
           (buffer-name))
          (condition-case ex
              (load project-local-init-file t)
            ('error
             (message
              "There was an error loading %s: %s"
              project-local-init-file
              (error-message-string ex))))))))

  (defun fn/find-project-file ()
    "Find the project's `.project.el'."
    (interactive)
    (when (projectile-project-p)
      (let* ((current-project-root (projectile-project-root))
             (project-file (expand-file-name fn/project-file current-project-root)))
        (find-file project-file))))

  (defun fn/find-project-locals-file ()
    "Find the project's `.project-locals.el'."
    (interactive)
    (when (projectile-project-p)
      (let* ((current-project-root (projectile-project-root))
             (project-locals-file (expand-file-name fn/project-local-file current-project-root)))
        (find-file project-locals-file))))

  (add-hook 'find-file-hook #'fn/load-project-file t)
  (add-hook 'find-dired-hook #'fn/load-project-file t)
  (add-hook 'dired-mode-hook #'fn/load-project-file t)

  (add-hook 'find-file-hook #'fn/load-project-local-file t)
  (add-hook 'find-dired-hook #'fn/load-project-local-file t)
  (add-hook 'dired-mode-hook #'fn/load-project-local-file t))

helm

The revolutionary package to find

(use-package helm
  :ensure t
  :unless noninteractive
  :diminish helm-mode
  :bind (("M-x" . helm-M-x)
         ("C-c f" . helm-recentf)
         ("C-h a" . helm-apropos)
         ("C-h r" . helm-info-emacs)
         ("C-x b" . helm-mini)
         ("C-x C-f" . helm-find-files)
         ("M-s o" . helm-occur)
         ("M-s i" . helm-imenu)
         ("C-c C-/" . helm-dabbrev))
  :config
  (require 'projectile nil t)

  (helm-mode t)

  (setq helm-yank-symbol-first t
        helm-mode-fuzzy-match nil
        helm-su-or-sudo "sudo"
        helm-input-idle-delay (/ 1 60.0) ;; 60fps
        helm-exit-idle-delay (/ 1 60.0)  ;; ditto
        helm-echo-input-in-header-line nil ;; If the theme does not block it
        helm-split-window-default-side 'same
        helm-display-function 'display-buffer
        helm-debug-root-directory fn/cache-dir)

  (setq helm-ff-file-name-history-use-recentf t
        helm-ff-auto-update-initial-value t
        helm-M-x-always-save-history t)

  (setq helm-adaptive-history-file (expand-file-name "helm-adaptive-history" fn/cache-dir)
        helm-adaptive-history-length 100)

  (helm-adaptive-mode t))

helm-projectile

A nice assist for projectile

(defconst fn/helm-projectile-package-dir (expand-file-name "helm-projectile/" fn/custom-module-dir)
  "My custom helm projectile package")

(with-eval-after-load 'helm
  (with-eval-after-load 'projectile
    (require 'dash)
    (require 'dash-functional)
    (require 's)
    (require 'f)

    (defconst fmc/completion-buffer-name "*Hacker Helm Completions"
      "Just a constant name for the completion buffer")


    (defface fmc/completion-label  '((t (:weight bold :height 1.1)))
      "Label face")

    (defface fmc/completion-delimiter '((t (:weight light :height 0.9)))
      "Delimiter face")

    (defface fmc/completion-description '((t (:weight extra-light :height 0.9)))
      "Description face")


    (defconst fmc/reverse-notation-separator ".."
      "My reversed separator")

    (defconst fmc/completion-separator ">>"
      "My completion separator")

    (defun fmc/uniquify-project-paths (project-paths)
      "Customize how projectile files and more are displayed"
      (let*
          ((fn-notation
            (lambda (path)
              (let ((fn-pieces (f-split path)))
                (string-join (reverse fn-pieces) fmc/reverse-notation-separator))))
           (relative-parent-path
            (lambda (path relative-path)
              (let
                  ((split-path (f-split path))
                   (split-relative-path (f-split relative-path)))
                (string-join
                 (-drop-last (length split-relative-path) split-path)
                 (f-path-separator)))))

           (as-pair
            (lambda (ish)
              (if (listp ish)
                  ish (cons ish ish))))
           (map-car
            (lambda (f pair)
              (cons (funcall f (car pair))
                    (cdr pair))))
           (pair-as-label
            (lambda (pairs)
              (let*
                  ((display-formatter
                    (lambda (name description)
                      (format "%-s %s %-s"
                              (propertize name 'font-lock-face 'fmc/completion-label)
                              (propertize fmc/completion-separator 'font-lock-face 'fmc/completion-delimiter)
                              (propertize description 'font-lock-face 'fmc/completion-description)))))
                (lambda (pair)
                  (let*
                      ((unique-path (car pair))
                       (full-path (cdr pair))
                       (parent-path
                        (funcall relative-parent-path
                                 full-path
                                 unique-path))

                       (display-name
                        (funcall fn-notation unique-path))
                       (display-description
                        (funcall fn-notation parent-path))

                       (display-label
                        (funcall display-formatter
                                 display-name
                                 display-description)))
                    (cons display-label (cdr pair)))))))
           (uniquify-paths
            (lambda (paths)
              ;; Ideally, this is just f-uniquify-alist but there is a minor contrivance
              (let*
                  ((is-dir
                    (lambda (path)
                      (string-equal (f-path-separator)
                                    (s-right 1 path))))

                   (swap-pair (lambda (pair)
                                (cons (cdr pair) (car pair))))
                   (map-pair
                    (lambda (f pair)
                      (cons (funcall f  (car pair)) (funcall f (cdr pair)))))

                   (remove-last-separator
                    (lambda (text)
                      (s-left (1- (length text)) text)))
                   (add-separator
                    (lambda (text)
                      (concat text (f-path-separator)))))
                (mapcar (-compose
                         swap-pair)
                        (if (-any is-dir paths)
                            ;; Remove separator, uniquify and add separator back
                            ;; Weird performance shiznit
                            (funcall
                             (-compose
                              (-partial #'mapcar (-partial map-pair add-separator))
                              #'f-uniquify-alist
                              (-partial #'mapcar remove-last-separator))
                             paths)
                          (f-uniquify-alist paths))))))
           (refined-paths  (funcall uniquify-paths project-paths)))
        (mapcar (-compose
                 (funcall pair-as-label refined-paths)
                 as-pair)
                refined-paths)))

    (defun fmc/custom-helm-completion (prompt choices)
      "Just a custom helm completion for projection"
      (prog1
          (helm-comp-read prompt (fmc/uniquify-project-paths choices)
                          :buffer fmc/completion-buffer-name
                          :must-match t)
        (kill-buffer fmc/completion-buffer-name)))

    (setq projectile-completion-system #'fmc/custom-helm-completion)))

helm-flx

Flex matching is strong

(use-package helm-flx
  :ensure t
  :after helm
  :config
  (helm-flx-mode t))

Help

Helper functions ere

command-log

A command log when needed

(use-package command-log-mode
    :ensure t
    :diminish command-log-mode
    :demand t
    :unless noninteractive
    :config
    (global-command-log-mode t))

keyfreq

Nice to know what key’s I press the most

(use-package keyfreq
  :ensure t
  :bind (:map fn-standard-prefix-map
              ("K" . keyfreq-show))
  :config
  (keyfreq-mode t)
  (keyfreq-autosave-mode t)

  (setq keyfreq-file (expand-file-name "keyfreq" fn/cache-dir)
        keyfreq-file-lock (expand-file-name "keyfreq.lock" fn/cache-dir)))

which-key

A mnemonic for key bindings

(use-package which-key
  :ensure t
  :diminish which-key-mode
  :demand t
  :unless noninteractive
  :config
  (which-key-mode t)
  (which-key-setup-side-window-bottom)

  (setq which-key-idle-delay 0.8

        which-key-separator " → "
        which-key-unicode-correction 3

        which-key-prefix-prefix ""
        which-key-show-prefix 'top
        which-key-echo-keystrokes nil

        which-key-show-remaining-keys nil

        which-key-sort-order 'which-key-description-order)


  (defun fn/which-key-prefix-command-replacement (kb)
    "My custom label for prefix command."
    (cons (car kb)
          (format
           "/ÎŁ-%2d/"
           (let ((which-key--current-prefix
                  (apply #'vector (listify-key-sequence (kbd (car kb))))))
             (length (which-key--get-current-bindings))))))


  ;; Mode Highlighting
  (require 's)

  ;; NOTE: Avoid changing height since it destroys the layout
  (defface fn/which-key-active-mode-face '((t . (:underline t :foreground "#dddddd")))
    "Face for active modes."
    :group 'fn)

  (defface fn/which-key-inactive-mode-face '((t . (:underline t :foreground "#444444")))
    "Face for inactive modes."
    :group 'fn)

  (defun fn/which-key-highlight-modes ()
    "Add active and inactive modes to `which-key-highlighted-command-list'."
    (message "Updating which-key mode highlighting.")
    (mapc
     (lambda (command-option)
       (when (and (consp command-option)
                  (s-ends-with-p "-mode$" (car command-option)))
         (setcdr command-option 'fn/which-key-inactive-mode-face)))
     which-key-highlighted-command-list)
    (mapc
     (lambda (minor-mode)
       (let* ((mode-name (symbol-name minor-mode))
                      (mode-regex (concat mode-name "$"))
                      (command-option
                       (assoc mode-regex which-key-highlighted-command-list)))
         (unless command-option
           (setq command-option
                 (cons mode-regex 'italic))
           (push command-option which-key-highlighted-command-list))
         (when (consp command-option)
           (setcdr command-option
                   (if (and (boundp minor-mode)
                            (symbol-value minor-mode))
                       'fn/which-key-active-mode-face 'fn/which-key-inactive-mode-face
                       )))))
     minor-mode-list)


    (let* ((generic-mode-regex ".*-mode$")
                   (command-option (assoc generic-mode-regex which-key-highlighted-command-list)))
      (unless command-option
        (add-to-list 'which-key-highlighted-command-list
                     (cons generic-mode-regex
                           'fn/which-key-inactive-mode-face)
                     t))))

  (advice-add 'which-key--init-buffer :before #'fn/which-key-highlight-modes)
  (add-hook 'after-init-hook #'fn/which-key-highlight-modes t)

  ;; Namespace Highlighting
  (defface fn/which-key-personal-namespace-face '((t . (:foreground "#9b59b6")))
    "Face for my personal namespace."
    :group 'fn)

  (add-to-list 'which-key-highlighted-command-list
               (cons "^f.*/" 'fn/which-key-personal-namespace-face))


  ;; Customized prefix display
  (add-to-list 'which-key-replacement-alist
               (cons (cons nil "Prefix Command")
                     #'fn/which-key-prefix-command-replacement)))

eldoc

Nice to have the documentation at any time in the buffer.

(use-package eldoc
  :diminish eldoc-mode
  :init
  (add-hook 'emacs-lisp-mode-hook 'turn-on-eldoc-mode)
  (add-hook 'lisp-interaction-mode-hook 'turn-on-eldoc-mode)
  (add-hook 'ielm-mode-hook 'turn-on-eldoc-mode)

  (add-hook 'org-mode-hook 'turn-on-eldoc-mode))

helm-descbinds

Another way to check bindings

(use-package helm-descbinds
  :ensure t
  :after helm
  :bind (("C-h b" . helm-descbinds))
  :config
  (setq helm-descbinds-window-style 'same))

helm-describe-modes

A nice way to describe the current modes

(use-package helm-describe-modes
  :ensure t
  :after helm
  :config
  (global-set-key [remap describe-mode] #'helm-describe-modes))

Profiler

Some things to help debug Emacs performance

(use-package profiler
  :if (not noninteractive)
  :init
  (defun fn/profiler-start ()
    "Start profiler with `cpu+mem'"
    (profiler-start 'cpu+mem))

  (add-hook 'after-init-hook #'fn/profiler-start)
  :commands (profiler-start profiler-report profiler-stop))

Packages

Packages to help manage packages

paradox

The package management improvement

(use-package paradox
  :ensure t
  :bind (:map fn-standard-prefix-map
              ("p" . paradox-list-packages))
  :init
  (setq package-gnupghome-dir (expand-file-name "gnupg" fn/cache-dir))
  :config
  (setq paradox-github-token t)

  (with-eval-after-load 'all-the-icons
    (fn/add-major-mode-icon
     'paradox-menu-mode
     (list 'all-the-icons-octicon "package" :v-adjust 0.0))))

elpa-mirror

Just in case things are down.

(use-package elpa-mirror
  :ensure t
  :commands (elpamr-create-mirror-for-installed fn/update-bootstrapped-packages)
  :init
  (defconst fn/elpamr-directory (expand-file-name "../.elpamr" user-emacs-directory)
    "Default `elpamr-default-output-directory' if none is set")

  (setq-default elpamr-default-output-directory fn/elpamr-directory)
  :config
  (require 'rx)
  (require 's)

  (defconst fn/backup-file-pattern
    (rx line-start
        (group-n 1 (one-or-more anything))
        "-"
        (group-n 2 (one-or-more (or digit ".")))
        "."
        (group-n 3 (one-or-more anything))
        line-end)
    "The `elpamr' backup file pattern.")

  (defun fn/update-bootstrapped-packages ()
    "Update `fn/bootstrap-packages' with files from `elpamr-default-output-directory'."
    (interactive)
    (let* ((raw-backup-package-files
            (mapcar #'file-name-nondirectory
                    (with-temp-buffer
                      (setq default-directory elpamr-default-output-directory)
                      (append
                       (file-expand-wildcards
                        (expand-file-name  "*.tar"))
                       (file-expand-wildcards
                        (expand-file-name  "*.el"))))))
           (backup-packages
            (mapcar (lambda (raw-backup-package-file)
                      (car (s-match-strings-all fn/backup-file-pattern raw-backup-package-file)))
                    raw-backup-package-files))
           (bootstrap-packages
            (mapcar (lambda (bootstrap-package)
                      (cons
                       (file-name-base (cdr bootstrap-package))
                       (cdr bootstrap-package)))
                    fn/bootstrap-packages)))
      (mapc
       (lambda (package)
         (pcase-let ((`(,package-file ,package-name _ ,package-extension) package))
           (let ((found-package (assoc-string package-name bootstrap-packages)))
             (when found-package
               (copy-file
                (expand-file-name package-file elpamr-default-output-directory)
                (expand-file-name (cdr found-package) fn/bootstrap-dir)
                t)
               (message "Updating bootstrapped package %s from %s"
                        package-name
                        package-file)))))
       backup-packages)))

  (advice-add 'elpamr-create-mirror-for-installed :after #'fn/update-bootstrapped-packages))

Artist

Visual aesthetics is also a functional thing as well

Screensaver

When idle time hits

fireplace

Warm and cozy feeling

(use-package fireplace
  :ensure t
  :bind (:map fn-standard-prefix-map
              ("F" . fireplace))
  :config
  (with-eval-after-load 'all-the-icons
    (fn/add-major-mode-icon
           'fireplace-mode
           (list 'all-the-icons-faicon "fire" :v-adjust -0.0))))

Coding

Whatever pertains to coding

rainbow-delimeter

Visual aid helps with very nested code

(use-package rainbow-delimiters
  :ensure t
  :defer t
  :init
  (add-hook 'prog-mode-hook 'rainbow-delimiters-mode)
  :config
  (set-face-attribute 'rainbow-delimiters-depth-1-face nil :foreground "dark orange")
  (set-face-attribute 'rainbow-delimiters-depth-2-face nil :foreground "deep pink")
  (set-face-attribute 'rainbow-delimiters-depth-3-face nil :foreground "chartreuse")
  (set-face-attribute 'rainbow-delimiters-depth-4-face nil :foreground "deep sky blue")
  (set-face-attribute 'rainbow-delimiters-depth-5-face nil :foreground "yellow")
  (set-face-attribute 'rainbow-delimiters-depth-6-face nil :foreground "orchid")
  (set-face-attribute 'rainbow-delimiters-depth-7-face nil :foreground "spring green")
  (set-face-attribute 'rainbow-delimiters-depth-8-face nil :foreground "sienna1"))

Display Buffer

My modification on display-buffer.

(progn
  (add-to-list
   'display-buffer-alist
   (cons
    (rx bos "*Process List*" eos)
    (cons 'display-buffer-same-window (list))))

  (add-to-list
   'display-buffer-alist
   (cons
    (rx bos "*Help*" eos)
    (cons 'display-buffer-same-window (list))))

  (add-to-list
   'display-buffer-alist
   (cons
    (rx bos "*Async Shell Command*")
    (cons 'display-buffer-same-window (list))))

  (add-to-list
   'display-buffer-alist
   (cons
    (rx bos "*shell" (* anything) "*")
    (cons 'display-buffer-same-window (list)))))

Organizer

Making Emacs more of an organization tool.

alert

Since Emacs doesn’t have dialog boxes, it is sufficient to have fringe or message alerts.

(use-package alert
  :ensure t
  :demand t
  :unless noninteractive
  :commands (alert fn/alert-color)
  :config
  (setq alert-default-style 'libnotify
        alert-log-messages t)

  (defun fn/alert-log-open ()
    "Open alert log."
    (interactive)
    (if (fboundp 'alert--log-open-log)
        (alert--log-open-log)
      (error "No alert logs yet")))

  ;; HACK: Bug #30 in alert to allow default style override
  (alert-add-rule)

  (cl-defun fn/alert-color (message &rest args &key color &allow-other-keys)
    "A custom alert that focuses on defining a fringe with COLOR key
with a hex value."
    (let* ((hex-color (replace-regexp-in-string "#" "" color))
                   (hex-symbol-name (format "alert-color--%s" hex-color))
                   (hex-symbol (or (intern-soft hex-symbol-name)
                                   (intern hex-symbol-name))))

      (unless (cdr (assoc hex-symbol alert-log-severity-functions))
        (add-to-list 'alert-log-severity-functions
                     (cons hex-symbol #'alert--log-trace)))

      (unless (cdr (assoc hex-symbol alert-severity-colors))
        (add-to-list 'alert-severity-colors (cons hex-symbol color)))

      (let ((color-properties
                     (list
                      :style 'fringe
                      :severity hex-symbol))
                    (colorless-properties
                     (cl-reduce
                      (lambda (val props)
                        (if (equal :color val)
                            (cdr props)
                          (cons val props)))
                      args
                      :from-end t
                      :initial-value (list))))
        (apply #'alert
               (append
                (list message)
                color-properties
                colorless-properties)))))

  (defun fn/alert-fringe-notify-message (info)
    "Log `fringe' style with `message'.
This is to support `fn/alert-color' if the color flash needs a
reminder."
    (message (plist-get info :message)))

  (advice-add 'alert-fringe-notify :after #'fn/alert-fringe-notify-message))

epa

Encryption is needed when working with sensitive files for personal or work.

;; Reference: http://conornash.com/2014/03/transparently-encrypt-org-files-in-emacs/

(use-package epa-file
  :demand t
  :config
  (epa-file-enable)

  (defun fn/backup-each-save-filter (filename)
    (let ((ignored-filenames '("\\.gpg$"))
          (matched-ignored-filename nil))
      (mapc
       (lambda (x)
         (when (string-match x filename)
           (setq matched-ignored-filename t)))
       ignored-filenames)
      (not matched-ignored-filename)))

  (setq backup-each-save-filter-function 'fn/backup-each-save-filter)

  (add-to-list 'auto-mode-alist (cons "\\.org\\.gpg\\'" 'org-mode)))

org

This makes more than a editor

Primarily I use org-drill and org-journal

(use-package org
  :ensure t
  :pin gnu
  :bind (("C-c l" . org-store-link)
         ("C-c a" . org-agenda)
         ("C-c h" . helm-org-in-buffer-headings)
         ("C-c c" . org-capture))
  :config
  (setq org-id-locations-file (expand-file-name "org-id-locations" fn/cache-dir))

  (setq org-catch-invisible-edits 'show)

  (add-to-list 'savehist-additional-variables 'org-insert-link-history)

  ;; Same windowed
  (defun fn/org-switch-to-buffer-other-window (&rest args)
    "This is an hacked form of `org-switch-to-buffer-other-window' to make it open in the same window."
    (org-no-popups
     (apply #'switch-to-buffer args)))

  (fset 'org-switch-to-buffer-other-window #'fn/org-switch-to-buffer-other-window)

  (defun fn/org-ignore-delete-other-windows (orig-fun &rest args)
    "Advice ORIG-FUN to ignore `delete-other-windows'"
    (prog2
        (advice-add 'delete-other-windows :override #'ignore)
        (apply orig-fun args)
      (advice-remove 'delete-other-windows #'ignore))))

;; (use-package org-plus-contrib
;;   :ensure t
;;   :pin gnu
;;   :after org
;;   :config
;;   nil)

org-src

Configuration for org-src

(with-eval-after-load 'org
  (setq org-src-window-setup 'current-window)

  (defun fn/org-src-inhibit-save-window-configuration ()
    "Disable org-src from saving the window configuration"
    ;; HACK: This uses an internal variable, might be unstable
    (setq org-src--saved-temp-window-config nil))

  (add-hook 'org-src-mode-hook #'fn/org-src-inhibit-save-window-configuration))

(with-eval-after-load 'org
  (setq org-todo-keywords
        `((sequence "TODO(t)" "PENDING(p)" "WAITING(w)" "|" "DONE(d)" "CANCELLED(c)")
          (sequence "EVENT(e)" "|" "ACCEPT(a)" "DECLINE(D)" )))

  (setq org-todo-keyword-faces
        ;; Color pattern from: http://stackoverflow.com/questions/12707492/add-custom-markers-to-emacs-org-mode
        '(("TODO" :background "#e74c3c" :foreground "#000000" :weight bold :box (:line-width 2 :style released-button))
          ("WAITING" :background  "#3498db" :foreground "#000000" :weight bold :box (:line-width 2 :style released-button))
          ("PENDING" :background "#f1c40f" :foreground "#000000" :weight bold :box (:line-width 2 :style released-button))
          ("DONE" :background "#2ecc71" :foreground "#000000" :weight bold :box (:line-width 2 :style released-button))
          ("CANCELLED" :background "lime green" :foreground "black" :weight bold :box (:line-width 2 :style released-button)))))

(with-eval-after-load 'org
  (with-eval-after-load 'desktop
    (add-to-list 'desktop-modes-not-to-save 'org-src-mode)))


(with-eval-after-load 'org
  (require 'dash)
  (make-variable-buffer-local
   (defvar fn/org-babel-get-src-block-info-mapper nil
     "A mapper for `org-babel-get-src-block-info'. Useful in manipulating its value"))


  (add-to-list 'safe-local-variable-values (cons 'fn/org-babel-get-src-block-info-mapper t))

  (defun fn/org-babel-get-src-block-info-mapper (orig-fun &rest args)
    "Maps over `org-babel-get-src-block-info' if `fn/org-babel-get-src-block-info-mapper' is present."
    (let ((value (apply orig-fun args)))
      (funcall
       (if (null fn/org-babel-get-src-block-info-mapper)
           'identity fn/org-babel-get-src-block-info-mapper)
       value)))

  (defun fn/org-info-file-to-tangle-mapper (info)
    "Defaults an empty `:tangle' option to its `:file' parameter which may be manipulated by `:output-dir'."
    (let* ((null-or-no-p
            (lambda (value) (if (or (null value) (string= value "no")) nil value)))
           (params (nth 2 info))
           (tangle-param
            (funcall null-or-no-p (cdr (assoc :tangle params))))
           (file-param
            (funcall null-or-no-p (cdr (assoc :file params))))
           (new-tangle-param
            (if (and file-param (null tangle-param)) file-param nil)))
      (if new-tangle-param
          (-replace-at 2 (cons (cons :tangle new-tangle-param) params) info)
        info)))

  (advice-add 'org-babel-get-src-block-info :around 'fn/org-babel-get-src-block-info-mapper))

org-capture

More configurations for the capture

(with-eval-after-load 'org
  nil ;; Configuration in personal.el
  )

org-agenda

More configuration for org-agenda

(with-eval-after-load 'org
  (setq org-agenda-span 14 ;; Fortnight
        org-agenda-window-setup 'current-window)

  ;; More at personal.el

  (defun fn/org-todo-entries ()
    "Get all entries from `org-todo-list' as cons pairs of id and text.
Useful for linking TODO entries."
    (save-window-excursion
      (org-todo-list 0)
      (let ((entries (list))
            (entry nil)
            (current-point (point)))
        (org-agenda-next-item 1)
        (while (/= (point) current-point)
          (setq current-point (point))
          (call-interactively 'org-store-link) ;; Trick agenda in storing the link into `org-stored-links'
          (setq entry (car org-stored-links))
          (push (cons (cadr entry) (car entry)) entries)
          (org-agenda-next-item 1))
        entries)))

  (defun fn/org-insert-completing-todo-link ()
    "Select a todo item to link.
Uses `fn/org-todo-entries' as the collection."
    (interactive)
    (let* ((entries (fn/org-todo-entries))
           (description (completing-read "Select a TODO entry: " ;; Bug in completing read?
                                         entries ;; Does not work if the todos are not unique, rare.
                                         nil
                                         t))
           (link (car (assoc-string description entries))))
      (org-insert-link nil link description)))

  (with-eval-after-load 'org-capture
    (define-key org-capture-mode-map (kbd "C-c m l") #'fn/org-insert-completing-todo-link))

  (define-key org-mode-map (kbd "C-c m l") #'fn/org-insert-completing-todo-link)

  (advice-add 'org-agenda-get-restriction-and-command :around #'fn/org-ignore-delete-other-windows))

org-roam

A knowledge base is nice.

(use-package org-roam
  :ensure t
  :after org
  :bind (("C-c C-i l" . org-roam-buffer-toggle)
         ("C-c C-i f" . org-roam-note-find)
         ("C-c C-i i" . org-roam-note-insert))
  :init
  (setq org-roam-db-location (expand-file-name "org-roam.db" fn/cache-dir)
        org-roam-db-gc-threshold most-positive-fixnum)
  :custom
  nil
  ;; Personal setup elsewhere
  ;; (setq org-roam-directory nil)
  :config
  (org-roam-setup)
  (run-with-idle-timer 0 nil #'org-roam-db-sync))

(use-package deft
  :ensure t
  :after org
  :bind ("C-c n d" . deft)
  :config
  (setq deft-recursive t
        deft-use-filter-string-for-filename t
        deft-default-extension "org"
        deft-directory org-roam-directory))

(use-package org-download
  :ensure t
  :after org
  :commands (org-download-screenshot org-download-yank)
  :bind( :map org-mode-map
         (("s-Y" . org-download-screenshot)
          ("s-y" . org-download-yank)))

  :config
  (setq org-image-actual-width 250)

  (add-hook 'dired-mode-hook 'org-download-enable))

org-refile

Some refiling actions perhaps?

(with-eval-after-load 'org
  (setq org-log-refile 'time
        org-refile-targets nil))

org-archiving

Archiving is needed to avoid bloating.

(with-eval-after-load 'org
  (when (boundp 'fn/org-dir))

  (defun fn/org-archive-done-agenda-tasks ()
    "Archive done agenda tasks."
    (message "Archiving done agenda tasks.")
    (org-map-entries
     (lambda ()
       (when (org-entry-is-done-p)
         (org-archive-subtree)))
     t 'agenda)
    (org-save-all-org-buffers)))

org-journal

Having a journal is good

(unless noninteractive
  (use-package org-journal
    :ensure t
    :after org
    :bind (:map fn-standard-prefix-map
                ("j" . org-journal-new-entry)) ;; C-c j conflicts with normal org-mode
    :config
    (setq org-journal-date-format "%Y-%b-%d %a" ;; YYYY-MMM-DD DAY
          org-journal-time-format "%T ") ;; HH:MM:SS and the space is required

    (setq org-journal-file-format "%Y-%m-%d.journal.org.gpg") ;; Encryption via epa
    (setq org-journal-find-file 'find-file)

    (defun fm/insert-private-file-headers ()
      (interactive)
      (add-file-local-variable-prop-line 'backup-inhibited t)
      (add-file-local-variable-prop-line 'auto-save-default nil)
      (goto-char (point-max)))

    (defun fm/insert-org-gpg-headers ()
      (interactive)
      (add-file-local-variable-prop-line
       'epa-file-encrypt-to (list "[email protected]"))
      (fm/insert-private-file-headers))

    (defun fmj/insert-summary-block ()
      "Insert summary block at point, this is pretty much #+begin_src except summary"
      (interactive)
      (let ((summary-block
             (string-join (list "#+BEGIN_SUMMARY"
                                "Something happened but I was too lazy to write it down"
                                "#+END_SUMMARY")
                          "\n")))
        (insert (concat summary-block "\n"))))

    (defun fmj/insert-org-journal-headers ()
      (interactive)
      (fm/insert-org-gpg-headers)

      (end-of-visual-line)
      (newline-and-indent)

      (when (string-match "\\(20[0-9][0-9]\\)-\\([0-9][0-9]\\)-\\([0-9][0-9]\\)"
                          (buffer-name))
        (let ((year  (string-to-number (match-string 1 (buffer-name))))
              (month (string-to-number (match-string 2 (buffer-name))))
              (day (string-to-number (match-string 3 (buffer-name))))
              (datim nil))
          (setq datim (encode-time 0 0 0 day month year))

          (insert "#+STARTUP: content\n")
          (insert (format-time-string
                   "#+TITLE: Journal Entry - %Y-%b-%d %a\n" datim))

          (fmj/insert-summary-block)


          (insert (format-time-string
                   "* %Y-%b-%d %a" datim)))))

    (auto-insert-mode t)
    (setq auto-insert-query t) ;; Don't ask, just put it in there
    (add-hook 'find-file-hook 'auto-insert)

    (add-to-list 'auto-insert-alist '(".*\.org\.gpg\\'" . fm/insert-org-gpg-headers))
    (add-to-list 'auto-insert-alist '(".*\.private.org\\'" . fm/insert-private-file-headers))
    (add-to-list 'auto-insert-alist '(".*\.journal.org.gpg\\'" . fmj/insert-org-journal-headers))

    (with-eval-after-load 'all-the-icons
      (all-the-icons)
      (fn/add-major-mode-icon
       'org-journal-mode
       (list 'all-the-icons-faicon "pencil-square-o" :v-adjust 0.0)))))

org-reveal

A nice presentation framework

(unless noninteractive
  (defconst fn/org-reveal-module-dir (expand-file-name "~/Modules/reveal.js")
    "reveal.js directory")

  (with-eval-after-load 'org
    (use-package ox-reveal
      :ensure t
      :if (file-exists-p fn/org-reveal-module-dir)
      :demand t
      :config
      (setq org-reveal-root (format "file:///%s" fn/org-reveal-module-dir))

      (defun fnr/tangle-buffer ()
        "Tangle current file via ox-reveal"
        (interactive)
        (let* ((current-file (buffer-file-name))
               (target-file (replace-regexp-in-string ".org" ".js" current-file))
               (target-lang "javascript"))
          (org-babel-tangle-file current-file target-file target-lang)))

      (defun fnr/export-buffer ()
        "Export current file via ox-reveal"
        (interactive)
        (message "Exporting %s" (buffer-file-name))
        (org-reveal-export-to-html nil))

      (defun fnr/reveal-export-buffer-on-save ()
        "Auto reveal export buffer on save"
        (interactive)
        (message "Adding hook to auto-export")
        (add-hook 'after-save-hook 'fnr/export-buffer t t))

      (define-minor-mode fn-reveal-editing-mode
        "Some editing enhancement when editing org-reveal files"
        :lighter " FnReveal"
        :init-value nil
        :global nil
        :keymap (let* ((map (make-sparse-keymap)))
                  (define-key map (kbd "C-c b C-p") 'fnr/tangle-buffer)
                  (define-key map (kbd "C-c b C-e") 'fnr/export-buffer)
                  map)))))

org-clock

Configuration for org-clock

(with-eval-after-load 'org
  (setq org-log-done 'time)

  (defun fn/clock-todo-change ()
    "A nice little hook that clocks in when a todo is change to PENDING"
    (let ((new-state org-state))
      (pcase new-state
        ("PENDING" (org-clock-in))
        (_ nil))))

  (add-hook 'org-after-todo-state-change-hook #'fn/clock-todo-change))

org-mobile

Mobile org document

(unless noninteractive
  (when (boundp 'fn/org-dir)
    (with-eval-after-load 'org
      (defconst fn/org-mobile-directory (expand-file-name "mobile" fn/org-dir)
        "My org mobile directory")

      (defconst fn/org-mobile-inbox-directory (expand-file-name "mobile-pull" fn/org-dir)
        "My org mobile directory")


      (setq org-mobile-directory  fn/org-mobile-directory
         org-mobile-inbox-for-pull fn/org-mobile-inbox-directory

         org-mobile-files (list
                           fn/org-todo-file
                           fn/org-event-file
                           ))

      (global-set-key (kbd "C-c n o p") #'org-mobile-push)
      (global-set-key (kbd "C-c n o l") #'org-mobile-pull))))

org-helm

Using helm to navigate org-mode.

(with-eval-after-load 'org
  (with-eval-after-load 'helm
    ;; Thanks to :
    ;; https://gist.githubusercontent.com/alphapapa/a3433c54a631b693ba9d/raw/f4f2462c8ccff7352037a12b720e5ccccaf94a7e/helm-org-tag-completion.el
    (add-to-list 'helm-completing-read-handlers-alist '(org-capture . fq/org-completing-read-tags))
    (add-to-list 'helm-completing-read-handlers-alist '(org-set-tags . fq/org-completing-read-tags))

    (defun fq/org-completing-read-tags (prompt coll pred req initial hist def inh)
      (if (not (string= "Tags: " prompt))
          ;; Not a tags prompt.  Use normal completion by calling
          ;; `org-icompleting-read' again without this function in
          ;; `helm-completing-read-handlers-alist'
          (let ((helm-completing-read-handlers-alist (rassq-delete-all
                                                      'fj/org-completing-read-tags
                                                      helm-completing-read-handlers-alist)))
            (org-icompleting-read prompt coll pred req initial hist def inh))
        ;; Tags prompt
        (let* ((initial (and (stringp initial)
                             (not (string= initial ""))
                             initial))
               (curr (when initial
                       (org-split-string initial ":")))
               (table (org-uniquify
                       (mapcar 'car org-last-tags-completion-table)))
               (table (if curr
                          ;; Remove current tags from list
                          (cl-delete-if (lambda (x)
                                          (member x curr))
                                        table)
                        table))
               (prompt (if initial
                           (concat "Tags " initial)
                         prompt)))
          (concat initial (mapconcat 'identity
                                     (nreverse (fq/helm-completing-read-multiple
                                                prompt table pred nil nil hist def
                                                t "Org tags" "*Helm org tags*" ":"))
                                     ":")))))

    (defun fq/helm-completing-read-multiple (prompt choices
                                                    &optional predicate require-match initial-input hist def
                                                    inherit-input-method name buffer sentinel)
      "Read multiple items with `helm-completing-read-default-1'. Reading stops
when the user enters SENTINEL. By default, SENTINEL is
\"*done*\". SENTINEL is disambiguated with clashing completions
by appending _ to SENTINEL until it becomes unique. So if there
are multiple values that look like SENTINEL, the one with the
most _ at the end is the actual sentinel value. See
documentation for `ido-completing-read' for details on the
other parameters."
      (let ((sentinel (or sentinel "*done*"))
            this-choice res done-reading)
        ;; Uniquify the SENTINEL value
        (while (cl-find sentinel choices)
          (setq sentinel (concat sentinel "_")))
        (setq choices (cons sentinel choices))
        ;; Read choices
        (while (not done-reading)
          (setq this-choice (helm-completing-read-default-1 prompt choices
                                                            predicate require-match initial-input hist def
                                                            inherit-input-method name buffer nil t))
          (if (equal this-choice sentinel)
              (setq done-reading t)
            (setq res (cons this-choice res))
            (setq prompt (concat prompt this-choice ":"))))
        res))))

org-tempo

Assist library when working with code blocks

(with-eval-after-load 'org
  (require 'org-tempo))

flyspell

Having a good spell checker is a must. I use flyspell although I have read issues about it

(use-package flyspell
  :ensure t
  :ensure-system-package aspell
  :if (and (not noninteractive)
           (not (eq system-type 'windows-nt)))
  :diminish flyspell-mode
  :hook (org-mode . flyspell-mode)
  :config
  (setq flyspell-default-dictionary "en_US"
        ispell-program-name "aspell"
        ispell-cmd-args  '("--sug-mode=ultra")))

langtool

Also a good grammar checker.

(use-package langtool
  :ensure t
  :ensure-system-package languagetool
  :if (not noninteractive)
  :bind (:map fn-standard-prefix-map
              ("C-l c" . langtool-check-buffer)
              ("C-l n" . langtool-goto-next-error)
              ("C-l p" . langtool-goto-previous-error))

  :config
  (setq langtool-default-language "en"
        langtool-mother-tongue "en"
        langtool-disabled-rules (list))

  (add-to-list 'langtool-disabled-rules "WHITESPACE_RULE")
  (add-to-list 'langtool-disabled-rules "EN_QUOTES")
  (add-to-list 'langtool-disabled-rules "EN_UNPAIRED_BRACKETS")

  (defun fn/langtool-clear-mode-line ()
    "Clear modeline for langtool."
    (interactive)
    (setq mode-line-process (remove '(t langtool-mode-line-message) mode-line-process))
    (force-mode-line-update))

  (defun fn/langtool-check-buffer ()
    "Error handled `langtool-check-buffer'."
    (interactive)
    (condition-case ex
        (progn
          (langtool--cleanup-process)
          (langtool-check-buffer))
      ('error (error-message-string ex))))

  (add-hook 'langtool-noerror-hook #'fn/langtool-clear-mode-line)
  (add-hook 'langtool-error-exists-hook #'fn/langtool-clear-mode-line))

ledger

Something to keep track of my finances

(use-package ledger-mode
  :ensure t
  :ensure-system-package ledger
  :if (not noninteractive)
  :config
  (setq ledger-clear-whole-transactions t)

  (add-to-list
   'ledger-reports
   '("monthly expense" "%(binary) -f %(ledger-file) -M reg ^expenses"))
  (add-to-list
   'ledger-reports
   '("weekly expense" "%(binary) -f %(ledger-file) -W reg ^expenses"))

  (with-eval-after-load 'all-the-icons
    (fn/add-major-mode-icon
     'ledger-mode
     '(all-the-icons-faicon "money" :v-adjust -0.1))
    (fn/add-major-mode-icon
     'ledger-report-mode
     '(all-the-icons-faicon "line-chart" :v-adjust -0.1))))

(use-package flycheck-ledger
  :ensure t
  :after (ledger flycheck)
  :hook (ledger-mode . flycheck-mode)
  :config
  (add-to-list 'flycheck-enabled-checkers 'ledger))

woman

Man pages are nice

(use-package woman
  :ensure t
  :if (and (not noninteractive)
           (executable-find "man"))
  :bind (:map fn-standard-prefix-map
              ("C-w" . woman))
  :config
  nil)

Programmer

Modes for programming

Support

Generic support for coding

Whitespace Cleanup

Some nice whitespace cleanup.

(use-package ws-butler
  :ensure t
  :defer t
  :init
  (add-hook 'prog-mode-hook 'ws-butler-mode)
  (add-hook 'org-mode-hook 'ws-butler-mode)
  :config
  nil)

Literate Programming

Enable some literate programming

(with-eval-after-load 'org
  (require 'ob-shell))

flycheck

Syntax checking is very important

(use-package flycheck
  :ensure t
  :unless noninteractive
  :diminish flycheck-mode
  :hook (prog-mode . flycheck-mode)
  :config
  (setq flycheck-highlighting-mode 'lines
        flycheck-check-syntax-automatically '(save mode-enabled 'idle-change)
        flycheck-checker-error-threshold nil))

(use-package flycheck-pos-tip
  :ensure t
  :after flycheck
  :config
  (flycheck-pos-tip-mode t))

(use-package flycheck-status-emoji
  :ensure t
  :after flycheck)

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

magit

Enough said, magit is the best git client you can get anywhere.

(use-package magit
  :ensure t
  :ensure-system-package git
  :if (and (version<= "24.4.4" emacs-version)
           (not noninteractive))
  :bind (("C-c g" . magit-status)
         ("M-g b" . magit-blame))
  :config
  (setq magit-repository-directories (list)
        magit-repository-directories-depth 1
        magit-push-always-verify t)

  (defun fq/display-magit-on-same-buffer (buffer)
    (if magit-display-buffer-noselect
        (magit-display-buffer-traditional buffer)
      (display-buffer-same-window buffer nil)))

  (setq magit-display-buffer-function #'fq/display-magit-on-same-buffer)

  (remove-hook 'magit-pre-display-buffer-hook 'magit-save-window-configuration)

  (add-to-list
   'display-buffer-alist
   (cons "\\`COMMIT_EDITMSG"
         (cons 'display-buffer-same-window (list))))

  (with-eval-after-load 'transient
    ;; Allow GPG signing in transient popup
    (setq transient-default-level 5))

  (with-eval-after-load 'projectile
    (make-variable-buffer-local 'magit-git-environment)

    (add-hook 'magit-process-mode-hook #'fn/load-project-local-file)
    (add-hook 'magit-status-mode-hook #'fn/load-project-local-file))

  (with-eval-after-load 'direnv
    (add-to-list 'direnv-non-file-modes 'magit-process-mode)))

(use-package git-timemachine
  :ensure t
  :bind (("M-g t" . git-timemachine))
  :config
  (progn
    (defun fn/noop (orig-fun &rest args)
      nil)

    (advice-add 'git-timemachine--erm-workaround :around #'fn/noop)))

lsp

LSP support

(use-package lsp-mode
  :ensure t
  :unless noninteractive
  :config
  (setq read-process-output-max (* 4 1024 1024))

  (setq lsp-session-file (expand-file-name ".lsp-session-v1" fn/cache-dir)
        lsp-print-performance t
        lsp-log-io nil

        lsp-headerline-breadcrumb-enable nil
        lsp-enable-file-watchers t
        lsp-file-watch-threshold 100000
        lsp-enable-xref t
        lsp-enable-completion-at-point t
        lsp-enable-identation t
        lsp-enable-on-type-formatting t))

(use-package lsp-ui
  :ensure t
  :after lsp-mode
  :hook (lsp-mode . lsp-ui-mode)
  :config
  (setq lsp-ui-doc-enable t))

dap

General debugging for Emacs

(use-package dap-mode
  :ensure t
  :unless noninteractive
  :config
  (setq dap-auto-configure-features '(sessions locals controls tooltip)))

Folding

Code folding is helpful

(use-package origami
  :ensure t
  :defer t
  :config
  (add-to-list 'origami-parser-alist '(json-mode . origami-javascript-style-parser)))

gist

GitHub integration with gists

(use-package gist
  :ensure t
  :defer t
  :commands (gist-list gist-buffer-private gist-buffer)
  :config
  (defun fn/ignore-gnutls (orig-fun &rest args)
    "Due to `https://github.com/sigma/gh.el/issues/73', ignore only `gnutls-available-p'
for every relevant command."
    (advice-add 'gnutls-available-p :around #'ignore)
    (apply orig-fun args)
    (advice-remove 'gnutls-available-p #'ignore))

  (advice-add 'gist-list :around #'fn/ignore-gnutls)
  (advice-add 'gist-buffer :around #'fn/ignore-gnutls)
  (advice-add 'gist-buffer-private :around #'fn/ignore-gnutls)

  (advice-add 'gist-list-reload :around #'fn/ignore-gnutls)
  (advice-add 'gist-fetch-current :around #'fn/ignore-gnutls)
  (advice-add 'gist-edit-current-description :around #'fn/ignore-gnutls)
  (advice-add 'gist-kill-current :around #'fn/ignore-gnutls)

  (with-eval-after-load 'all-the-icons
    (fn/add-major-mode-icon
     'gist-list-mode
     (list 'all-the-icons-octicon "checklist" :v-adjust 0.0))))

pastebin

Paste bin integration

(defconst fn/emacs-pastebin-package-dir (expand-file-name "emacs-pastebin/" fn/custom-module-dir)
  "My emacs pastebin package directory.")

(unless noninteractive
  (use-package neopastebin
    :load-path fn/emacs-pastebin-package-dir
    :bind (:map fn-standard-prefix-map
                ("C-y n" . pastebin-new)
                ("C-y l" . pastebin-list-buffer-refresh))
    :init
    (setq pastebin-data-dir (expand-file-name "pastebin-data" fn/cache-dir))))

ag

The silver searcher is a fine tool

(use-package ag
  :ensure t
  :ensure-system-package (ag . the_silver_searcher)
  :commands (ag)
  :if (not noninteractive)
  :config
  (setq ag-highlight-search t
        ag-reuse-window t
        ag-reuse-buffers t)

  (add-to-list
   'display-buffer-alist
   (cons "\\`\\*ag [^z-a]*\\*\\'"
         (cons 'display-buffer-same-window (list))))

  (with-eval-after-load 'all-the-icons
    (fn/add-major-mode-icon
     'ag-mode
     (list 'all-the-icons-faicon "binoculars" :v-adjust -0.1))))

(use-package helm-ag
  :ensure t
  :demand t
  :after (helm ag)
  :config
  nil)

rg

Ripgrep is another good search tool

(use-package ripgrep
  :ensure t
  :ensure-system-package (rg . ripgrep)
  :if (not noninteractive)
  :config
  nil)

helm-swoop

Something to speed up searching

(use-package helm-swoop
  :ensure t
  :after helm
  :bind (("M-i" . helm-swoop)
         ("C-c M-i" . helm-multi-swoop))
  :config
  (define-key helm-swoop-map (kbd "C-r") 'helm-previous-line)
  (define-key helm-swoop-map (kbd "C-s") 'helm-next-line)
  (define-key helm-multi-swoop-map (kbd "C-r") 'helm-previous-line)
  (define-key helm-multi-swoop-map (kbd "C-s") 'helm-next-line)

  (setq helm-swoop-pre-input-function (-const "")))

(use-package wgrep-helm
  :ensure t
  :after helm
  :config
  (setq wgrep-auto-save-buffer t))

Generic Outline

Generic support for outlining.

(unless noninteractive
  (use-package outline
    :ensure t
    :demand t
    :config
    nil)

  (use-package outline-magic
    :ensure t
    :defer t
    :after (outline)
    :config
    (define-key outline-minor-mode-map (kbd "<C-tab>") 'outline-cycle)))

REST Client

I need REST.

(use-package restclient
  :ensure t
  :defer t
  :mode (("\\.rest\\'" . restclient-mode)
         ("\\.rst\\'" . restclient-mode))
  :config
  (add-hook 'restclient-mode-hook 'whitespace-mode)

  (defun fn/restclient-indent-function ()
    "Thanks to `https://github.com/pashky/restclient.el/issues/85'"
    (require 'js)
    (setq-local indent-line-function 'js-indent-line))

  (add-hook 'restclient-mode-hook 'fn/restclient-indent-function)

  (with-eval-after-load 'restclient
    (with-eval-after-load 'all-the-icons
      (fn/add-major-mode-icon
       'restclient-mode
       (list 'all-the-icons-faicon "arrow-circle-o-up" :v-adjust -0.1))))

  (use-package company-restclient
    :ensure t
    :after (company restclient)
    :config
    (add-to-list 'company-backends 'company-restclient))

  (use-package ob-restclient
    :ensure t
    :after (org restclient)
    :config
    (add-to-list 'org-babel-load-languages '(restclient . t)))

  (with-eval-after-load 'outline
    (defun fn/outline-restclient-setup ()
      "Setup `outline' with `restclient'."
      (outline-minor-mode +1)
      (setq-local outline-regexp "###"))

    (add-hook 'restclient-mode-hook 'fn/outline-restclient-setup)))

smerge

Resolving diffs the nice way.

(use-package smerge-mode
  :defer t
  :config
  (setq smerge-command-prefix "C-c v")

  (progn
    (define-key smerge-mode-map (kbd "n") #'smerge-next)
    (define-key smerge-mode-map (kbd "p") #'smerge-prev)

    (define-key smerge-mode-map (kbd "RET") #'smerge-keep-current)
    (define-key smerge-mode-map (kbd "m") #'smerge-keep-mine)
    (define-key smerge-mode-map (kbd "o") #'smerge-keep-other)

    (define-key smerge-mode-map (kbd "E") #'smerge-ediff)))

Protobuf

Protobuf support

(use-package protobuf-mode
  :ensure t
  :ensure-system-package protoc
  :if (not noninteractive)
  :config
  nil)

YAML

YAML editing

(use-package yaml-mode
  :ensure t
  :defer t)

TOML

TOML editing

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

JSON

JSON support

(use-package json-mode
  :ensure t
  :defer t
  :mode ("\\.json\\'" . json-mode)
  :config
  (when (executable-find "jsonlint")
    (add-to-list 'flycheck-enabled-checkers 'json-jsonlint)))

(use-package json-snatcher
  :ensure t
  :after (json-mode))

(use-package json-reformat
  :ensure t
  :after (json-mode))

(use-package web-beautify ;; A bit more nodejs
  :ensure t
  :if (and (not noninteractive)
           (executable-find "js-beautify"))
  :after json-mode)

GraphQL

GraphQL support

(use-package graphql-mode
  :ensure t
  :defer t)

Markdown

Markdown editing

(use-package markdown-mode
  :ensure t
  :defer t
  :config
  (when (executable-find "multimarkdown")
    (setq markdown-command "multimarkdown"
          markdown-command-needs-filename nil)))

CSV

CSV moder

(use-package csv-mode
  :ensure t
  :unless noninteractive
  :config
  nil)

String Inflection

Support for rotating between camel case and what not

(unless noninteractive
  (use-package string-inflection
    :ensure t
    :defer t
    :bind (("C-c C-u" . string-inflection-all-cycle))
    :config
    nil))

Thesaurus

A little thesaurus help.

(use-package powerthesaurus
  :ensure t
  :commands (powerthesaurus-lookup-word))

OpenGL

OpenGL shader writing support

(use-package glsl-mode
  :ensure t
  :unless noninteractive
  :config
  nil
  )

PlantUML

Support for making diagrams

(defconst fn/plantuml-file (expand-file-name "~/Modules/plantuml.jar" ))

(when (file-exists-p fn/plantuml-file)
  (use-package plantuml-mode
    :ensure t
    :defer t
    :config
    nil)

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

  (with-eval-after-load 'org
    (require 'ob-plantuml)

    (add-to-list 'org-src-lang-modes '("plantuml" . plantuml))
    (add-to-list 'org-babel-load-languages '(plantuml . t))

    (setq org-plantuml-jar-path fn/plantuml-file)))

Helper

Specific helper modes

Elisp

Mode

This editor is the mode

(require 'dash)

(defun fn/find-definition-at-point ()
  "This is find-function-at-point and find-variable-at-point meld into one"
  (interactive)
  (-if-let
      (point-pair (cond
                   ((symbolp (variable-at-point))
                    (find-variable-noselect
                     (variable-at-point)
                     nil))
                   ((function-called-at-point) (condition-case ex
                                                   (find-function-noselect (function-called-at-point) t)
                                                 ('error nil)))
                   (t nil)))
      (switch-to-buffer (car point-pair))
    (message "Could not find function or symbol definition or might be builtin")))

(let ((custom-keymap (fn/make-work-keymap emacs-lisp-mode-map)))
  (define-key custom-keymap (kbd "j") #'fn/find-definition-at-point))

Formatter

Not really but nice to have for a lisp language

(use-package elisp-format
  :ensure t
  :defer t
  :disabled t
  :config
  (define-key emacs-lisp-mode-map (kbd "C-c C-f") 'elisp-format-region)

  (make-variable-buffer-local
   (defvar fn/elisp-format-on-save t
     "Enable elisp formatting on save"))

  (defun fn/elisp-format-buffer-on-save ()
    "Format elisp on save"
    (when fn/elisp-format-on-save
      (with-current-buffer (current-buffer)
        (message "Elisp formatting buffer %s" (current-buffer))
        (elisp-format-buffer))))

  (add-hook 'before-save-hook #'fn/elisp-format-buffer-on-save))

Testing

Some testing libraries

(use-package buttercup
  :ensure t
  :defer t)

Project

Some packages to help making packages

(defconst fn/header2-module-dir (expand-file-name "header2/" fn/custom-module-dir)
  "Archived `header2' packages.")

(defconst fn/epl-module-dir (expand-file-name "epl/" fn/custom-module-dir)
  "Archived `epl' packages.")

(use-package epl
  :load-path fn/epl-module-dir
  :demand t)

(use-package cask-mode
  :ensure t
  :defer t)

(with-eval-after-load 'all-the-icons
  (fn/add-major-mode-icon
   'cask-mode
   (list 'all-the-icons-faicon "glass" :v-adjust 0.1)))

(unless noninteractive
  (use-package header2
    :load-path fn/header2-module-dir
    :demand t
    :functions auto-make-header
    :init
    (add-hook 'emacs-lisp-mode-hook 'auto-make-header)))

(use-package overseer
  :ensure t
  :defer t
  :hook (emacs-lisp-mode . overseer-mode)
  :diminish 'overseer-mode)

XML

nxml-mode the default please.

(use-package nxml-mode
  :mode ("\\.xml\\'" . nxml-mode)
  :hook (nxml-mode . company-mode)
  :config
  (defun fn/nxml-prettify-on-save ()
    (when (and (eq major-mode 'nxml-mode)
               (require 'sgml-mode))
      (sgml-pretty-print (point-min) (point-max))))

  (add-hook 'before-save-hook #'fn/nxml-prettify-on-save))

SQL

Client

If enterprise grade support is needed.

(use-package ejc-sql
  :ensure t
  :ensure-system-package lein
  :defer t
  :after (sql)
  :functions (ejc-create-connection)
  :commands (ejc-connect)
  :config
  (setq ejc-set-rows-limit 1000
        nrepl-sync-request-timeout 60))

Literate Programming

Sometimes it’s just easier to get a scratch pad.

(with-eval-after-load 'org
  (with-eval-after-load 'sql
    (require 'ob-sql) ;; Default

    (use-package ob-sql-mode
      :ensure t
      :defer t
      :after (org sql)
      :config
      nil ;; None yet
      )))

Formatting

Some utility when editing SQL files

(use-package format-sql
  :ensure t
  :ensure-system-package format-sql
  :defer t
  :if (not noninteractive)
  :commands (format-sql-buffer format-sql-region))

Web

The defacto mode for web development

(use-package web-mode
  :ensure t
  :mode ("\\.html\\'" . web-mode)
  :config
  (setq web-mode-enable-auto-pairing t
        web-mode-enable-auto-closing t
        web-mode-enable-current-element-highlight t
        web-mode-enable-current-column-highlight t))

(use-package company-web
  :ensure t
  :after (company web-mode)
  :config
  (add-to-list 'company-backends 'company-web-html))

(use-package emmet-mode
  :ensure t
  :hook ((web-mode . emmet-mode)
         (css-mode . emmet-mode))
  :config
  (setq emmet-self-closing-tag-style " /")

  (with-eval-after-load 'js2-mode
    (setq emmet-expand-jsx-className? t)))

(use-package sass-mode
  :ensure t
  :defer t
  :config
  nil)

(use-package scss-mode
  :ensure t
  :defer t
  :config
  nil)

JavaScript

Mode

The ultimate JS mode

(use-package js2-mode
  :ensure t
  :interpreter (("node" . js2-mode))
  :mode (("\\.\\(js\\)$" . js2-mode)
         ("\\.\\(jsx\\)$" . js2-jsx-mode))
  :defer t
  :config
  (add-hook 'js-mode-hook 'js2-minor-mode)
  (add-hook 'js2-mode-hook 'subword-mode)
  (add-hook 'js2-jsx-mode-hook 'subword-mode)

  (setq js2-highlight-level 3
        js2-mode-show-parse-errors nil
        js2-mode-show-strict-warnings nil

        js2-include-node-externs t
        js2-include-browser-externs t)

  (with-eval-after-load 'all-the-icons
    (fn/add-major-mode-icon
     'js2-jsx-mode
     (list 'all-the-icons-alltheicon "javascript" :v-adjust 0.1))))

Package Manger

npm helper

(use-package npm-mode
  :ensure t
  :bind (:map fn-standard-prefix-map
              ("n" . npm-mode))
  :if (and (not noninteractive)
           (executable-find "npm"))
  :init
  (setq npm-mode-command-prefix "C-c n n"))

Linter

My linter if you will for JS.

(unless noninteractive
  (with-eval-after-load 'flycheck
    (with-eval-after-load 'js2-mode
      ;; Thanks to https://emacs.stackexchange.com/questions/21205/flycheck-with-file-relative-eslint-executable
      (defun fq/use-eslint-from-node-modules ()
        (let* ((root (locate-dominating-file
                      (or (buffer-file-name) default-directory)
                      ".projectile"))
               (eslint (and root
                            (expand-file-name "node_modules/eslint/bin/eslint.js"
                                              root))))
          (when (and eslint (file-executable-p eslint))
            (setq-local flycheck-javascript-eslint-executable eslint))))

      (add-to-list 'flycheck-checkers 'javascript-eslint)
      (add-hook 'flycheck-mode-hook #'fq/use-eslint-from-node-modules))))

Formatter

Formatter for js, choose your poison. I tried the three and still am.

(use-package web-beautify
  :ensure t
  :after js2-mode)

(use-package jsfmt
  :ensure t
  :after js2-mode
  :config
  (let ((custom-keymap (fn/make-work-keymap js2-mode-map)))
    (define-key custom-keymap (kbd "b") #'jsfmt)))

(use-package eslint-fix
  :ensure t
  :after js2-mode
  :config
  (defun fn/eslint-fix-after-save ()
    "Apply eslint fix after save"
    (add-hook 'after-save-hook #'eslint-fix nil t))

  ;; (add-hook 'js2-mode-hook #'fn/eslint-fix-after-save)
  )

REPL

Got to have those REPLs

(use-package nodejs-repl
  :ensure t
  :bind (("C-c C-n C-c" . nodejs-repl-send-buffer)
         ("C-c C-n C-r" . nodejs-repl-send-region)
         ("C-c C-n C-e" . nodejs-repl-send-last-sexp))
  :after js2-mode)

(use-package skewer-mode
  :defer t
  :diminish skewer-mode
  :bind (("C-c K" . run-skewer))
  :ensure t
  :init
  (add-hook 'js2-mode-hook 'skewer-mode)
  (add-hook 'css-mode-hook 'skewer-css-mode)
  (add-hook 'html-mode-hook 'skewer-html-mode))

JSON

JSON support

(use-package json-snatcher
  :ensure t
  :after js2-mode
  :bind (("C-c C-g" . jsons-print-path)))

Literate Programming

Let’s support literate programming for JS

(with-eval-after-load 'org
  (with-eval-after-load 'js2-mode
    (add-to-list 'org-babel-load-languages '(js . t))))

Python

Mode

My favorite programming language, unassuming like Marcy from Peanuts

(use-package python
  :ensure t
  :ensure-system-package (python)
  :defer t
  :config
  nil)

Formatter

Pep me bro

(use-package py-autopep8
  :ensure t
  :defer t
  :after python)

Autocomplete

The newer Python IDE, Jedi, much easier to grok.

This requires pip and the packages virtualenv.

(use-package jedi
  :ensure t
  :after elpy
  :config
  nil)

(use-package company-jedi
  :ensure t
  :after (jedi company)
  :config
  (add-to-list 'company-backends 'company-jedi))

IDE

Making Python an IDE

(use-package elpy
  :ensure t
  :defer t
  :init
  (add-hook 'python-mode-hook 'elpy-enable)

  (add-hook 'elpy-mode-hook 'flycheck-mode)
  (add-hook 'elpy-mode-hook 'py-autopep8-enable-on-save)
  :config
  (setq elpy-python-command "python"
        elpy-rpc-python-command "python"
        elpy-rpc-backend "jedi")

  (setq elpy-interactive-python-command "ipython")
  (elpy-use-ipython)

  (setq elpy-modules (delq 'elpy-module-flycheck elpy-modules))

  (require 'py-autopep8)
  (add-hook 'elpy-mode-hook 'py-autopep8-enable-on-save))

Project

virtualenv is a must

(use-package python-environment
  :ensure t
  :init
  (setq python-environment-directory (expand-file-name "python-environments" fn/cache-dir)))
(use-package virtualenv
  :ensure t
  :after python)

Or the most updated

(use-package virtualenvwrapper
  :ensure t
  :after python)

Literate Programming

Let’s add a link to org-babel

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

Haskell

Mode

The defacto for Haskell development

(use-package haskell-mode
    :ensure t
    :ensure-system-package ghci
    :defer t
    :init
    ;; (add-hook 'haskell-mode-hook 'interactive-haskell-mode)
    (add-hook 'haskell-mode-hook 'haskell-auto-insert-module-template)
    (add-hook 'haskell-mode-hook 'haskell-decl-scan-mode)
    :config
    ;; (require 'haskell-interactive-mode)
    (require 'haskell-process)
    ;; Reset mapping as it does more damage than good
    (setq haskell-cabal-mode-map (make-keymap)
       interactive-haskell-mode-map (make-keymap))

    (define-key haskell-mode-map (kbd "<f8>") 'haskell-navigate-imports)

    (define-key haskell-mode-map (kbd "C-c C-c") 'haskell-compile)
    (define-key haskell-cabal-mode-map (kbd "C-c C-c") 'haskell-compile)

    ;; Haskell bindings
    (define-key haskell-mode-map (kbd "C-c C-l") 'haskell-process-load-or-reload)
    (define-key haskell-mode-map (kbd "C-`") 'haskell-interactive-bring)
    (define-key haskell-mode-map (kbd "C-c C-t") 'haskell-process-do-type)
    (define-key haskell-mode-map (kbd "C-c C-i") 'haskell-process-do-info)
    (define-key haskell-mode-map (kbd "C-c C-c") 'haskell-process-cabal-build)
    (define-key haskell-mode-map (kbd "C-c C-k") 'haskell-interactive-mode-clear)
    (define-key haskell-mode-map (kbd "C-c c") 'haskell-process-cabal)

    ;; Cabal bindings
    ;; (define-key haskell-cabal-mode-map (kbd "C-`") 'haskell-interactive-bring)
    ;; (define-key haskell-cabal-mode-map (kbd "C-c C-k") 'haskell-interactive-mode-clear)
    ;; (define-key haskell-cabal-mode-map (kbd "C-c C-c") 'haskell-process-cabal-build)
    ;; (define-key haskell-cabal-mode-map (kbd "C-c c") 'haskell-process-cabal)

    ;; Interactive Haskell
    ;; (define-key interactive-haskell-mode-map (kbd "C-c M-.") 'haskell-mode-goto-loc)
    ;; (define-key interactive-haskell-mode-map (kbd "C-c M-t") 'haskell-mode-show-type-at)

    ;; (setq haskell-stylish-on-save t)

    ;; (setq interactive-haskell-mode t)

    (setq haskell-process-suggest-remove-import-lines t
       haskell-process-auto-import-loaded-modules t
       haskell-process-log t
       haskell-process-suggest-hoogle-imports t
       haskell-process-type 'stack-ghci
       haskell-interactive-mode-eval-mode 'haskell-mode)

    (when (executable-find "hoogle")
      (add-hook 'haskell-interactive-mode-hook 'haskell-hoogle-start-server)

      (advice-add 'haskell-process-load-file :after 'haskell-interactive-mode-clear)

      ;; Hoogle
      (define-key haskell-mode-map (kbd "C-c b h") 'hoogle)
      (define-key interactive-haskell-mode-map (kbd "C-c b h") 'hoogle)))

(use-package helm-hoogle
  :ensure t
  :after (haskell-mode helm))

Haskell Process Load Fix

(with-eval-after-load 'haskell-mode
  ;;;  https://github.com/haskell/haskell-mode/pull/1605
  (defun haskell-process-load-complete (session process buffer reload module-buffer &optional cont)
    "Handle the complete loading response. BUFFER is the string of
text being sent over the process pipe. MODULE-BUFFER is the
actual Emacs buffer of the module being loaded."
    (when (get-buffer (format "*%s:splices*" (haskell-session-name session)))
      (with-current-buffer (haskell-interactive-mode-splices-buffer session)
        (erase-buffer)))
    (let* ((ok (cond
                ((haskell-process-consume
                  process
                  "Ok, \\(?:[0-9]+\\) modules? loaded\\.$")
                 t)
                ((haskell-process-consume
                  process
                  "Ok, \\(?:[a-z]+\\) module loaded\\.$") ;; for ghc 8.4
                 t)
                ((haskell-process-consume
                  process
                  "Failed, \\(?:[0-9]+\\) modules? loaded\\.$")
                 nil)
                ((haskell-process-consume
                  process
                  "Ok, modules loaded: \\(.+\\)\\.$")
                 t)
                ((haskell-process-consume
                  process
                  "Failed, modules loaded: \\(.+\\)\\.$")
                 nil)
                (t
                 (error (message "Unexpected response from haskell process.")))))
           (modules (haskell-process-extract-modules buffer))
           (cursor (haskell-process-response-cursor process))
           (warning-count 0))
      (haskell-process-set-response-cursor process 0)
      (haskell-check-remove-overlays module-buffer)
      (while
          (haskell-process-errors-warnings module-buffer session process buffer)
        (setq warning-count (1+ warning-count)))
      (haskell-process-set-response-cursor process cursor)
      (if (and (not reload)
               haskell-process-reload-with-fbytecode)
          (haskell-process-reload-with-fbytecode process module-buffer)
        (haskell-process-import-modules process (car modules)))
      (if ok
          (haskell-mode-message-line (if reload "Reloaded OK." "OK."))
        (haskell-interactive-mode-compile-error session "Compilation failed."))
      (when cont
        (condition-case-unless-debug e
            (funcall cont ok)
          (error (message "%S" e))
          (quit nil))))))

Motion

The paredit for haskell, this weirdly needs exec-path-from-shell to work

(use-package shm
  :ensure t
  :disabled t
  :after haskell-mode
  :init
  (add-hook 'haskell-mode-hook 'structured-haskell-mode)
  :config
  (define-key shm-map (kbd "C-j") 'shm/newline-indent)
  (define-key shm-map (kbd "M-a") 'shm/goto-parent)
  (define-key shm-map (kbd "M-e") 'shm/goto-parent-end)
  (define-key shm-map (kbd "C-+") 'shm/add-operand)
  (define-key shm-map (kbd "M-r") 'shm/raise)
  (define-key shm-map (kbd "M-^") 'shm/delete-indentation)
  (define-key shm-map (kbd "M-k") 'shm/kill)
  (define-key shm-map (kbd "C-y") 'shm/yank)
  (define-key shm-map (kbd "M-k") 'shm/kill-line)


  ;; SHM with interactive-haskell
  (defun fn/shm-interactive-config (&rest args)
    "Setup SHM with interactive-haskell-mode"
    (require 'shm-case-split)
    (define-key shm-map (kbd "C-c C-s") 'shm/case-split))

  (eval-after-load 'haskell-interactive-mode #'fn/shm-interactive-config))

Autocomplete

Company for Haskell

(unless noninteractive
  (with-eval-after-load 'haskell-mode
    (with-eval-after-load 'company
      (use-package company-ghc
        :ensure t
        :after company
        :config
        (add-to-list 'company-backends 'company-ghc))

      (use-package company-ghci
        :ensure t
        :after company
        :config
        (add-to-list 'company-backends 'company-ghci)))))

Formatter

Formatter for Haskell

(use-package hindent
  :ensure t
  :after haskell-mode
  :init
  (add-hook 'haskell-mode-hook #'hindent-mode)
  :config
  (setq hindent-style "johan-tibell")

  (defun fn/hindent-before-save ()
    "Reformat before saving."
    (interactive)
    (add-hook 'before-save-hook 'hindent-reformat-buffer t t)))

Linter

Syntax checker for Haskell

(with-eval-after-load 'haskell-mode
  (with-eval-after-load 'flycheck
    (use-package flycheck-haskell
      :ensure t
      :demand t
      :config
      (flycheck-haskell-setup))

    (use-package flycheck-stack
      :ensure t
      :after flycheck-haskell
      :config
      nil)))

Build Tools

Maybe stack or hack?

Literate Programming

Literate programming for ease

(defconst fn/ob-haskell-package-dir (expand-file-name "ob-haskell/" fn/custom-module-dir)
  "My ob-haskell package dir.")

(unless noninteractive
  (with-eval-after-load 'haskell-mode
    (with-eval-after-load 'org
      (use-package ob-haskell
        :load-path fn/ob-haskell-package-dir
        :disabled t
        :demand t
        :config
        (add-to-list 'org-babel-load-languages '(haskell . t)))

      (defconst fn/haskell-file-extension ".hs"
        "The de facto haskell file extension.")

      (defun fn/add-haskell-file-extension (name)
        "Add the extension of .hs to a file or buffer NAME."
        (if (string/ends-with name fn/haskell-file-extension)
            name (concat name fn/haskell-file-extension)))

      (defvar fn/org-haskell-mode-hook nil
        "Hook when buffer is haskellized.")

      (defun fn/haskellize-buffer-file (&optional buffer)
        "Renames an BUFFER with a .hs extension if it doesn't have one."
        (interactive)
        (with-current-buffer (or buffer (current-buffer))
          (save-buffer)
          (let ((name (buffer-name))
                        (file-name (buffer-file-name)))
            (if (not (and file-name (file-exists-p file-name)))
                (error "Buffer '%s' has no backing file" name)
              (let ((haskellized-name (fn/add-haskell-file-extension name))
                            (haskellized-file-name (fn/add-haskell-file-extension file-name)))
                (cond
                 ((get-buffer haskellized-name)
                  (error "A buffer named '%s' already exists" haskellized-name))
                 ((string-equal name haskellized-name)
                  (message "Buffer %s is already haskellized" haskellized-name))
                 (t
                  (rename-file file-name haskellized-file-name t)
                  (rename-buffer haskellized-name)
                  (set-visited-file-name haskellized-file-name)
                  (set-buffer-modified-p nil)
                  (message "Buffer %s is now haskellized" haskellized-name))))))))

      (defun fn/org-haskell-buffer-p (&optional buffer)
        "Check if BUFFER is an org-haskell buffer."
        (with-current-buffer (or buffer (current-buffer))
          (and (eq major-mode 'haskell-mode)
               (fboundp 'org-src-edit-buffer-p)
               (org-src-edit-buffer-p))))

      (defun fn/haskellize-org-haskell-buffer (&rest _)
        "Haskellize org haskell buffer."
        (when (fn/org-haskell-buffer-p)
          (fn/haskellize-buffer-file (current-buffer))
          (run-hooks 'fn/org-haskell-mode-hook)))

      (defun fn/save-org-haskell-buffer (&rest _)
        "Save haskell buffer along with the edit buffer."
        (when (fn/org-haskell-buffer-p)
          (save-buffer)))

      (defun fn/cleanup-org-haskell-buffer (orig-fun &rest args)
        "Cleanup the org-haskell buffer when exiting the edit buffer."
        (let ((org-haskell-file-name (buffer-file-name))
                      (org-haskell-buffer-p (fn/org-haskell-buffer-p)))
          (prog1
              (apply orig-fun args)
            (when (and (file-exists-p org-haskell-file-name) org-haskell-buffer-p)
              (delete-file org-haskell-file-name)))))

      (defun fn/haskell-process-load-or-reload ()
        "Invoke reload process without switching buffers"
        (save-window-excursion
          (haskell-process-load-or-reload))

        (defun fn/haskell-reload-on-save ()
          "Reload interactive haskell process on save."
          (add-hook 'after-save-hook 'fn/haskell-process-load-or-reload t t))))))

Erlang

Some minor support for working with Erlang

(use-package erlang
  :ensure t
  :defer t
  :config
  nil)

(use-package company-erlang
  :ensure t
  :after (company erlang)
  :init
  (add-hook 'erlang-mode-hook #'company-erlang-init))

Elixir

The dynamic Haskell in my opinion… or was it Python or Ruby

(use-package elixir-mode
  :ensure t
  :defer t
  :hook ((elixir-mode . subword-mode)
         (elixir-mode . lsp))
  :config
  (with-eval-after-load 'lsp-mode
    (setq lsp-elixir-dialyzer-enabled nil
          lsp-elixir-ls-download-url "https://github.com/elixir-lsp/elixir-ls/releases/download/v0.15.1/elixir-ls-v0.15.1.zip"))

  (with-eval-after-load 'all-the-icons
    (fn/add-major-mode-icon
     'elixir-mode
     (list 'all-the-icons-faicon "flask" :v-adjust -0.1))))

 (use-package alchemist
   :ensure t
   :hook (elixir-mode . alchemist-mode)
   :config
   (setq alchemist-test-status-modeline nil
         alchemist-hooks-compile-on-save t
         alchemist-iex-program-name "iex")

   (with-eval-after-load 'company
     (add-hook 'alchemist-iex-mode-hook 'company-mode)))

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

Rust

A close C alternative that I can chew on

(use-package rustic
  :ensure
  :unless noninteractive
  :bind (:map rustic-mode-map
              ("M-j" . lsp-ui-imenu)
              ("M-?" . lsp-find-references)
              ("C-c C-c l" . flycheck-list-errors)
              ("C-c C-c a" . lsp-execute-code-action)
              ("C-c C-c r" . lsp-rename)
              ("C-c C-c q" . lsp-workspace-restart)
              ("C-c C-c Q" . lsp-workspace-shutdown)
              ("C-c C-c s" . lsp-rust-analyzer-status))
  :config
  (setq rustic-format-display-method 'ignore
        rustic-format-on-save nil)

  (setq rustic-lsp-client 'lsp-mode
        rustic-lsp-setup-p nil)

  (when (executable-find "rust-analyzer")
    (setq rustic-lsp-server 'rust-analyzer)
    (setq lsp-rust-server 'rust-analyzer))

  (define-key rustic-mode-map (kbd "C-c C-c r") #'rustic-cargo-test-rerun)
  (define-key rustic-cargo-test-mode-map (kbd "r") #'rustic-cargo-test-rerun)
  (define-key rustic-compilation-mode-map (kbd "r") #'rustic-cargo-test-rerun)

  (add-hook 'rustic-mode-hook #'lsp-deferred)

  (with-eval-after-load 'lsp-mode
    (setq lsp-signature-auto-activate nil
          lsp-inlay-hint-enable nil)

    (setq lsp-rust-analyzer-server-args '("--parallel")
          lsp-rust-analyzer-cargo-watch-enable t
          lsp-rust-analyzer-display-lifetime-elision-hints-enable "skip_trivial"
          lsp-rust-analyzer-display-chaining-hints nil
          lsp-rust-analyzer-display-parameter-hints nil
          lsp-rust-analyzer-display-lifetime-elision-hints-use-parameter-names nil
          lsp-rust-analyzer-display-closure-return-type-hints nil
          lsp-rust-analyzer-display-parameter-hints nil
          lsp-rust-analyzer-display-reborrow-hints nil)))

(use-package flycheck-rust
  :ensure
  :after (flycheck rustic)
  :config
  (setq rustic-flycheck-clippy-params "--message-format=json")
  (push 'rustic-clippy flycheck-checkers))

Ruby

Mode

Built in ruby-mode is enough

Completion

Still using company with robe

(unless noninteractive
  (with-eval-after-load 'robe
    (with-eval-after-load 'company
      (add-to-list 'company-backends 'company-robe))))

Linter

Integrating rubocop here

(use-package rubocop
  :ensure t
  :init
  (add-hook 'ruby-mode-hook #'rubocop-mode)
  :config
  nil)

(use-package rubocopfmt
  :ensure t
  :init
  (add-hook 'rubocop-mode-hook #'rubocopfmt-mode)
  :config
  (setq rubocopfmt-use-bundler-when-possible nil))

Editing

Some editing tools

(unless noninteractive
  (use-package ruby-tools
    :ensure t
    :init
    (add-hook 'ruby-mode-hook #'ruby-tools-mode)
    :config
    nil))

Project

Ruby has it’s own environment

(use-package projectile-rails
  :ensure t
  :after ruby-mode
  :hook (ruby-mode . projectile-rails-mode)
  :config
  (add-to-list
   'display-buffer-alist
   (cons
    (rx bos "*rails" (* anything))
    (cons 'display-buffer-same-window (list)))))

(use-package ruby-test-mode
  :ensure t
  :hook (ruby-mode  . ruby-test-mode)
  :config
  nil)

LaTeX

LaTeX editing for my files

Mode

Actual mode to do the job

(with-eval-after-load 'latex
  (add-hook 'latex-mode-hook 'flyspell-mode)

  (add-to-list 'TeX-command-list '("XeLaTeX" "%`xelatex%(mode)%' %t" TeX-run-TeX nil t)))

(use-package auctex
  :ensure t
  :defer t
  :init
  (add-hook 'LaTeX-mode-hook 'turn-on-reftex)
  (add-hook 'latex-mode-hook 'turn-on-reftex)
  (setq reftex-plug-into-AUCTex t)
  :config
  (setq TeX-auto-save t
        TeX-parse-self t)

  (setq-default TeX-master nil))

Autocomplete

Autocomplete obviously

(use-package company-auctex
  :ensure t
  :after auctex
  :config
  (company-auctex-init))

Linter

Something similar but for spelling

(with-eval-after-load 'auctex
  (with-eval-after-load 'flyspell
    (add-hook 'latex-mode-hook #'flyspell-mode)))

REPL

Something like that but rather a preview for every edit

(use-package latex-preview-pane
  :ensure t
  :after auctex
  :config
  (define-key latex-mode-map (kbd "C-c C-p") 'latex-preview-pane-mode))

Git Gutter

Git gutter support for Emacs

(use-package git-gutter
  :ensure t
  :defer t
  :init
  (add-hook 'prog-mode-hook #'git-gutter-mode)
  :commands (git-gutter-mode)
  :bind (("C-x g" . git-gutter:toggle)
         ("C-x n" . git-gutter:next-hunk)
         ("C-x p" . git-gutter:previous-hunk)
         ("C-x v s" . git-gutter:stage-hunk)
         ("C-x v r" . git-gutter:revert-hunk))
  :config
  (setq git-gutter:window-width 1
        git-gutter:modified-sign "/"
        git-gutter:added-sign "+"
        git-gutter:deleted-sign "-"))

Random Was Alone

Packages that are just useful

w3m

Web browsing is a must

(use-package w3m
  :ensure t
  :if (and (not noninteractive)
           (executable-find "w3m"))
  :bind (:map fn-standard-prefix-map
              ("w w" . w3m)
              ("w s" . w3m-search-new-session)
              ("w e" . w3m-external-view-current-url)
              ("w g" . w3m-goto-url))
  :init
  (setq w3m-search-engine-alist (list))

  (defcustom fn/w3m-ignore-arrived-url-functions (list)
    "Functions that check if the url is saved for privacy."
    :type 'hook)

  (defconst fn/w3m-ignore-arrived-hosts (list)
    "Ignore the urls of the arrived hosts.")

  (defcustom fn/w3m-exclude-ignore-search-engine (list)
    "Exclude search engine from being ignored."
    :type 'list)
  :config
  ;; Main
  (setq w3m-home-page "https://duckduckgo.com"
        w3m-init-file (expand-file-name "emacs-w3m.config" fn/cache-dir)
        w3m-use-cookies nil
        w3m-confirm-leaving-secure-page nil
        w3m-cookie-file (expand-file-name "w3m-cookie" fn/cache-dir))

  (setq w3m-command-arguments (list))

  ;; Tabs
  (setq w3m-add-tab-number t
        w3m-make-new-session nil

        w3m-use-header-line t
        w3m-use-header-line-title t
        w3m-show-graphic-icons-in-header-line nil

        w3m-use-title-buffer-name t)

  ;; Util
  (defun fn/w3m-ignore-search-engine-pages (url &rest args)
    "Ignore search engine pages as defined by `w3m-search-engine-alist'."
    (ignore-errors ;; Ignore special urls such as //:about or whatnot
      (let* ((url-parts (w3m-parse-http-url url))
                     (url-host (elt url-parts 1)))
        (cl-some
         (lambda (pair)
           (pcase-let ((`(,name ,search-url) pair))
             (let* ((search-parts (w3m-parse-http-url search-url))
                            (search-host (elt search-parts 1)))
               (and (not (member name fn/w3m-exclude-ignore-search-engine))
                    (string= search-host url-host)))))
         w3m-search-engine-alist))))

  (defun fn/w3m-ignore-special-pages (url)
    "Ignore special pages such like `about://'."
    (pcase url
      ((pred (string-prefix-p "about://")) t)
      (_ nil)))


  ;; Search Engine
  (require 'w3m-search)

  (setq w3m-search-default-engine "duckduckgo")


  (add-to-list 'w3m-search-engine-alist
               (list "duckduckgo" "https://duckduckgo.com?q=%s"))
  (add-to-list 'w3m-search-engine-alist
               (list "youtube" "https://www.youtube.com/results?search_query=%s"))
  (add-to-list 'w3m-search-engine-alist
               (list "wikipedia" "http://en.wikipedia.org/wiki/Special:Search?search=%s"))


  ;; Proxy
  (require 'w3m-proc)


  ;; Conkeror / Lnum
  (require 'w3m-lnum)

  (add-hook 'w3m-mode-hook #'w3m-lnum-mode t)


  ;; History
  (require 'w3m-hist)

  (setq w3m-y-reuse-history-elements t
        w3m-arrived-file (expand-file-name "w3m-arrived" fn/cache-dir)
        w3m-arrived-db-size 2000
        w3m-keep-cache-size 1500)


  (defun fn/w3m-ignore-arrived-add-url (orig-fun &rest args)
    "Check whether to save the url using `fn/w3m-ignore-arrived-url-functions' as basis.
Each function predicate takes is the same arguments as `w3m-arrived-add'
Good for privacy and cleanliness."
    (if (not (apply #'run-hook-with-args-until-success
                    (append
                     (list 'fn/w3m-ignore-arrived-url-functions)
                     args)))
        (apply orig-fun args)
      (message "Not saving arrived url %s" (car args))
      nil))

  (advice-add 'w3m-arrived-add :around #'fn/w3m-ignore-arrived-add-url)


  (define-minor-mode fn/w3m-private-mode
    "A simple minor mode to indicate private browsing in `w3m'."
    :lighter " w3m-private"
    :init-value nil
    :global t
    :keymap (make-sparse-keymap)
    (defun fn/w3m-ignore-all-arrived-url (&rest args)
      "Ignore all urls when `fn/w3m-private-mode' is active"
      (and (boundp 'fn/w3m-private-mode)
           fn/w3m-private-mode))

    (add-hook 'fn/w3m-ignore-arrived-url-functions #'fn/w3m-ignore-all-arrived-url))

  (defun fn/w3m-ignore-arrived-host (url &rest args)
    "Ignore if url host matches and `fn/w3m-ignore-arrived-hosts'.
Primary check comes from `w3m-parse-http-url' by direct string match."
    (condition-case ex
        (let* ((parts (w3m-parse-http-url url))
                       (host (elt parts 1)))
          (not (cl-notany
                (lambda (check-host)
                  (string= check-host host))
                fn/w3m-ignore-arrived-hosts)))
      ('error
       (message
        "There was an error parsing with fn/w3m-ignore-arrived-host: %s"
        (error-message-string ex)))))

  (add-hook 'fn/w3m-ignore-arrived-url-functions #'fn/w3m-ignore-search-engine-pages)
  (add-hook 'fn/w3m-ignore-arrived-url-functions #'fn/w3m-ignore-arrived-host)


  ;; Bookmarking
  (require 'w3m-bookmark)

  (setq w3m-bookmark-file (expand-file-name "w3m-bookmark.html" fn/setting-dir)
        w3m-bookmark-default-section "Misc")


  ;; Persistent Session
  (require 'w3m-session)

  (setq w3m-session-file (expand-file-name "w3m-session" fn/cache-dir))


  ;; Util
  (defun fn/w3m-new-buffer ()
    "Opens a new, empty w3m buffer.
Thanks to https://www.emacswiki.org/emacs/WThreeMTabs"
    (interactive)
    (w3m-goto-url-new-session "about://"))


  ;; Key (re)binding
  (define-key w3m-mode-map (kbd "q") 'w3m-delete-buffer)
  (define-key w3m-mode-map (kbd "Q") 'w3m-quit)

  (define-key w3m-mode-map (kbd "c") 'w3m-lnum-print-this-url)
  (define-key w3m-mode-map (kbd "f") 'w3m-lnum-follow)

  (define-key w3m-mode-map (kbd "h") 'w3m-history)
  (define-key w3m-mode-map (kbd "H") 'w3m-db-history)

  (define-key w3m-mode-map (kbd "M-p") 'w3m-previous-buffer)
  (define-key w3m-mode-map (kbd "M-n") 'w3m-next-buffer)

  (define-key w3m-mode-map (kbd "i") 'w3m-toggle-inline-image)
  (define-key w3m-mode-map (kbd "I") 'w3m-toggle-inline-images)

  (define-key w3m-mode-map (kbd "t") 'fn/w3m-new-buffer)
  (unbind-key (kbd "t") w3m-lnum-mode-map)

  (define-key w3m-mode-map (kbd "V") 'w3m-bookmark-view)
  (define-key w3m-mode-map (kbd "v") 'w3m-bookmark-add-current-url)

  (define-key w3m-mode-map (kbd "{") 'w3m-previous-buffer)
  (define-key w3m-mode-map (kbd "}") 'w3m-next-buffer)

  (unbind-key "<right>" w3m-mode-map)
  (unbind-key "<left>" w3m-mode-map)

  (unbind-key "<up>" w3m-mode-map)
  (unbind-key "<down>" w3m-mode-map)

  (define-key w3m-mode-map (kbd "S") 'w3m-search-new-session)
  (define-key w3m-mode-map (kbd "s") 'w3m-search)
  (define-key w3m-mode-map (kbd "RET") 'w3m-view-this-url)
  (define-key w3m-mode-map (kbd "M-RET") 'w3m-view-this-url-new-session)

  (define-key w3m-mode-map (kbd ";") #'fn/w3m-fontify-summary)

  (with-eval-after-load 'all-the-icons
    (fn/add-major-mode-icon
     'w3m-mode
     (list 'all-the-icons-faicon "globe" :v-adjust -0.1))))

elfeed

Reading RSS feeds in Emacs than in your email

(use-package elfeed
  :ensure t
  :unless noninteractive
  :bind (:map fn-standard-prefix-map
              ("E E" . elfeed)
              ("E u" . elfeed-update))
  :config
  (setq elfeed-db-directory (expand-file-name ".elfeed" fn/cache-dir)))

(use-package elfeed-org
  :after elfeed
  :config
  (elfeed-org))

slack

Slack is awesome.

(defconst fn/slack-map (fn/make-prefixed-keymap (kbd "s") fn-standard-prefix-map)
  "My custom slack map.")

(use-package slack
  :ensure t
  :defer t
  :bind (:map fn/slack-map
              ("s" . slack-start))
  :config
  (setq slack-request-timeout 60)

  (setq slack-enable-emoji nil
        slack-buffer-emojify nil
        slack-typing-visibility 'never
        slack-prefer-current-team t
        slack-buffer-function #'switch-to-buffer)

  (progn
    ;; Thanks https://github.com/yuya373/emacs-slack/issues/175#issuecomment-300159618
    (defun fn/slack-unread-rooms ()
      "Get unread rooms from slack."
      (let ((team (slack-team-select)))
        (cl-loop for team in (list team)
                 append
                 (with-slots (groups ims channels) team
                   (cl-remove-if
                    #'(lambda (room)
                        (not (< 0 (oref room unread-count-display))))
                    (append ims groups channels))))))

    (defun fn/slack-goto-unread-room ()
      "Quickly visit an unread room in slack."
      (interactive)
      (let ((unread-rooms (fn/slack-unread-rooms)))
        (if (null unread-rooms)
            (error "No unread rooms in slack. Silence is good.")
          (slack-room-create-buffer
           (car unread-rooms)
           (slack-team-find (oref (car unread-rooms) team-id)))))))

  (progn ;; Force subscription
    (defun fn/slack-room-all-subcribedp (_ team)
      "Overrides `slack-room-subscribedp' to always return
      non-nil to force subscription."
      team)

    (advice-add 'slack-room-subscribedp :override #'fn/slack-room-all-subcribedp))

  (with-eval-after-load 'slack
    (with-eval-after-load 'flyspell
      (add-hook 'slack-edit-message-mode-hook #'flyspell-mode)))

  (with-eval-after-load 'alert
    (defun fn/slack-message-custom-notifier (message room team)
      "My custom notication for slack given MESSAGE, ROOM and TEAM.
What I do is use the fringe to notify me instead of `libnotify'."
      (when (and (not (slack-message-minep message team))
                 (or (slack-im-p room)
                     (and (slack-group-p room) (slack-mpim-p room))
                     (slack-room-subscribedp room team)
                     (string-match (format "@%s" (plist-get (oref team self) :name))
                                   (or (slack-message-body message team) ""))))
        (let* ((team-name (oref team name))
               (room-name (slack-room-name room))
               (text (slack-message-to-alert message team))
               (user-name (slack-message-sender-name message team))
               (out (fn/chat-log team-name room-name user-name text)))
          (fn/alert-color out
                          :category (intern team-name)
                          :color "#3a417a"))))

    (setq slack-message-custom-notifier #'fn/slack-message-custom-notifier
          slack-message-custom-delete-notifier #'fn/slack-message-custom-notifier))

  (with-eval-after-load 'all-the-icons
    (fn/add-major-mode-icon
     'slack-mode
     (list 'all-the-icons-faicon "commenting-o" :v-adjust 0.0))
    (fn/add-major-mode-icon
     'slack-edit-message-mode
     (list 'all-the-icons-faicon "pencil" :v-adjust 0.0)))

  (progn
    (define-key fn/slack-map (kbd "q") #'slack-ws-close)

    (define-key fn/slack-map (kbd "t") #'slack-change-current-team)

    (define-key fn/slack-map (kbd "b k") #'slack-buffer-kill)
    (define-key fn/slack-map (kbd "b r") #'slack-select-rooms)
    (define-key fn/slack-map (kbd "b c") #'slack-channel-select)
    (define-key fn/slack-map (kbd "b i") #'slack-im-select)
    (define-key fn/slack-map (kbd "b g") #'slack-group-select)
    (define-key fn/slack-map (kbd "b t") #'slack-group-select)

    (define-key fn/slack-map (kbd "u b") #'slack-select-unread-rooms)
    (define-key fn/slack-map (kbd "u u") #'fn/slack-goto-unread-room))

  (progn
    (define-key slack-mode-map (kbd "C-c r r") #'slack-message-remove-reaction)
    (define-key slack-mode-map (kbd "C-c r s") #'slack-message-show-reaction-users)
    (define-key slack-mode-map (kbd "C-c r a") #'slack-message-add-reaction)

    (define-key slack-mode-map (kbd "C-c C-m") #'slack-message-write-another-buffer)

    (define-key slack-mode-map (kbd "C-c m e") #'slack-message-edit)

    (define-key slack-mode-map (kbd "C-c m u") #'slack-room-update-messages))

  (progn
    (define-key slack-edit-message-mode-map (kbd "C-c e m") #'slack-message-embed-mention)
    (define-key slack-edit-message-mode-map (kbd "C-c e c") #'slack-message-embed-channel)))

emms

This controls the music player.

(defconst fn/emms-map (fn/make-prefixed-keymap (kbd "C-p") fn-standard-prefix-map)
  "My custom prodigy map.")

(use-package emms
  :ensure t
  :ensure-system-package (vlc mpd mp3info ogginfo)
  :defer t
  :init
  (setq emms-directory (expand-file-name "emms" fn/cache-dir))
  :bind (:map fn/emms-map
              ("C-p" . emms)
              ("p n" . emms-next)
              ("p p" . emms-previous)
              ("p q" . emms-pause)
              ("p s" . emms-start)
              ("p S" . emms-stop))
  :config
  (emms-minimalistic)
  (emms-standard)

  (when (require 'emms-player-simple))

  (setq emms-player-list nil)

  (setq emms-source-file-default-directory (expand-file-name "~/Musicbox/"))

  (setq emms-info-asynchronously nil
        emms-playlist-buffer-name "*Music*")

  (setq emms-playlist-default-major-mode 'emms-playlist-mode)

  (when (require 'emms-mark)
    (add-hook 'emms-playlist-mode-hook #'emms-mark-mode))

  (when (require 'emms-history)
    (emms-history-load))

  (when (require 'emms-browser))

  (when (require 'emms-volume)
    (emms-volume-minor-mode t)

    (setq emms-volume-mode-timeout 1))

  (add-to-list 'emms-info-functions 'emms-info-mp3info)
  (add-to-list 'emms-info-functions 'emms-info-ogginfo)

  (fn/add-major-mode-icon
   'emms-playlist-mode
   (list 'all-the-icons-faicon "music" :v-adjust -0.1))

  (progn
    (global-set-key (kbd "C-c -") 'emms-volume-mode-plus)
    (global-set-key (kbd "C-c +") 'emms-volume-mode-minus)

    (global-set-key (kbd "C-c n C-e s") 'emms-start)
    (global-set-key (kbd "C-c n C-e S") 'emms-stop)))

MPD Integration

I prefer mpd as a music player as it is more stable.

(when (executable-find "mpd")
  (with-eval-after-load 'emms
    (require 'emms-player-mpd)

    (setq emms-player-mpd-supported-regexp
          (regexp-opt '(".ogg" ".mp3" ".wav" ".mpg" ".mpeg" ".wmv" ".wma"
                        ".mov" ".avi" ".divx" ".ogm" ".asf" ".mkv" "http://" "mms://"
                        ".rm" ".rmvb" ".mp4" ".flac" ".vob" ".m4a" ".flv" ".ogv" ".pls")))

    (add-to-list 'emms-info-functions 'emms-info-mpd)
    (add-to-list 'emms-player-list 'emms-player-mpd)

    (with-eval-after-load 'prodigy
      (defcustom fn/emms-mpd-cache-dir (expand-file-name "emms-mpd" fn/cache-dir)
        "Emms cache directory."
        :type 'directory)

      (defcustom fn/emms-mpd-config-file (expand-file-name "mpd.conf" fn/emms-mpd-cache-dir)
        "Mpd config file"
        :type 'file)

      (defcustom fn/emms-mpd-port 38700
        "Mpd port config file"
        :type 'number)

      (defun fn/emms-mpd-update-conf (&rest args)
        "Create/update `fn/emms-mpd-config-file' with new
      configurations."
        (interactive)
        (make-directory fn/emms-mpd-cache-dir t)

        (setq emms-player-mpd-server-port nil
              emms-player-mpd-server-name (expand-file-name "socket" fn/emms-mpd-cache-dir))

        (fn/prodigy-create-mpd-conf
         fn/emms-mpd-config-file
         `(("music_directory" . ,(expand-file-name emms-source-file-default-directory))
           ("playlist_directory" . ,(expand-file-name "Playlist"))
           ("connection_timeout" . 30)
           ("state_file" . ,(expand-file-name "state" fn/emms-mpd-cache-dir))
           ("pid_file" . ,(expand-file-name "pid" fn/emms-mpd-cache-dir))
           ("sticker_file" . ,(expand-file-name "sticker.sql" fn/emms-mpd-cache-dir))
           ("port" . ,fn/emms-mpd-port)
           ("bind_to_address" . ,emms-player-mpd-server-name)
           ("audio_output" . (("type" . "pulse")
                              ("name" . "My PULSE Device")))
           ("database" . (("plugin" . "simple")
                          ("path" . ,(expand-file-name "db" fn/emms-mpd-cache-dir)))))))

      (defun fn/emms-mpd-on-start (&rest args)
        "On start of mpd server.."
        (setq emms-volume-change-function 'emms-volume-mpd-change)
        (emms-player-mpd-connect)
        (emms-cache-set-from-mpd-all))

      (defun fn/emms-mpd-on-stop (&rest args)
        "On stop of mpd server."
        (setq emms-volume-change-function 'emms-volume-mpd-change)
        (emms-player-mpd-disconnect))

      (defconst fn/emms-mpd-service-name "emms-mpd"
        "Emms mpd prodigy service name.")

      (fn/prodigy-define-service
       :name fn/emms-mpd-service-name
       :tags '(mpd emms)
       :init #'fn/emms-mpd-update-conf
       :on-start #'fn/emms-mpd-on-stop
       :on-stop #'emms-player-mpd-disconnect
       :args `("--no-daemon"
               "--stderr"
               "--verbose"
               ,fn/emms-mpd-config-file)

       :bind-name "emms-mpd"
       :bind-map fn/prodigy-map
       :bind (kbd "m m"))

      (prodigy-start-service (prodigy-find-service fn/emms-mpd-service-name)))))

VLC Integration

A good fallback but not that I depend on it.

(when (executable-find "vlc")
  (with-eval-after-load 'emms
    (require 'emms-player-vlc)

    ;; Fallback
    (add-to-list 'emms-player-list 'emms-player-vlc t)
    (add-to-list 'emms-player-list 'emms-player-vlc-playlist t)

    ;; Remove default advice for vlc which does not work
    (advice-remove 'emms-player-vlc-start #'ad-Advice-emms-player-vlc-start)))

Scripts

Each optional section is optional but scripts

Clean Home

Make sure my Emacs home is clean

(require 'f)

(defconst fn/user-emacs-home-files
  (list
   "bootstrap"
   "bootstrapper.el"
   ".cache"
   ".setting"
   "config.el"
   "config.org"
   "extra"
   "system"
   ".git"
   ".gitignore"
   "init.el"
   "init-standard.el"
   "LICENSE"
   "lib"
   "personal.el"
   ".projectile"
   ".project-locals.el"
   "README.org")
  "Known Emacs home files")


(defun fn/clean-user-emacs-home ()
  "Make sure Emacs only has the following files as specified by `fn/user-emacs-home-files'"
  (interactive)
  (message "Cleaning emacs home.")
  (mapc
   (lambda (home-file)
     (unless (member home-file fn/user-emacs-home-files )
       (message "Deleting trash file %s" home-file)
       (f-delete (expand-file-name home-file user-emacs-directory) t)))
   (mapcar ;; You can use `directory-files' but you have to filter `.' and `..'
    'f-filename
    (append
     (f-files user-emacs-directory)
     (f-directories user-emacs-directory))))
  (message "All clean."))

(add-hook 'after-init-hook 'fn/clean-user-emacs-home)

Custom Emacs

Bootstrap different configurations of Emacs for reviewing expert configurations and trying different modes

Configurable

Let’s bootstrap from different configurations dynamically

(defun fn/bootstrap-from-dir ()
  (interactive)
  (setq new-home (read-directory-name "What Emacs config would you like to boot?" "~"))
  (fn/bootstrap-new-emacs new-home))


(defvar fn/previous-bootstrap-dir nil)

(defun fn/store-bootstrap-dir (new-home)
  "Store previously bootstrap directory"
  (setq fn/previous-bootstrap-dir new-home))

(advice-add 'fn/bootstrap-new-emacs :after 'fn/store-bootstrap-dir)


(defun fn/bootstrap-previous ()
  "Bootstrap the previously chosen one, `fn/previous-bootstrap-dir'"
  (interactive)
  (fn/bootstrap-new-emacs fn/previous-bootstrap-dir))

Itself

Load another copy of this configuration for vanity sake I suppose?

(defun fn/bootstrap-itself ()
  (interactive)
  (fn/bootstrap-new-emacs nil))

Experimental

Just a random build for myself

(setq fn/experimental-home "~/.fmacs.d/")

(defun fn/bootstrap-experimental ()
  (interactive)
  (fn/bootstrap-new-emacs fn/experimental-home))

Utility Commands

Some commands to get me here and there

(defun fn/save-and-kill-buffer ()
  "Save and kill the buffer in one command"
  (interactive)
  (call-interactively 'save-buffer)
  (call-interactively 'kill-this-buffer))

Sensitive Mode

When editiing gpg or sensitve files, I would like to avoid backing it up so here is a minor mode to do so.

(define-minor-mode fq/sensitive-mode
  "For sensitive files like password lists.
It disables backup creation and auto saving.

With no argument, this command toggles the mode.
Non-null prefix argument turns on the mode.
Null prefix argument turns off the mode."
  ;; The initial value.
  nil
  ;; The indicator for the mode line.
  " Sensitive"
  ;; The minor mode bindings.
  nil
  (if (symbol-value fq/sensitive-mode)
      (progn
        ;; disable backups
        (set (make-local-variable 'backup-inhibited) t)
        ;; disable auto-save
        (if auto-save-default
            (auto-save-mode -1)))
                                        ;resort to default value of backup-inhibited
    (kill-local-variable 'backup-inhibited)
                                        ;resort to default auto save setting
    (if auto-save-default
        (auto-save-mode 1))))

(add-to-list 'auto-mode-alist '("\\.gpg\\'" . fq/sensitive-mode))

Dired Shortcuts

Some shortcuts with the common folder

(defun fn/dired-download-dir ()
  "Just a quick shortcut to my directory folder"
  (interactive)
  (dired "~/Downloads"))

(defun fn/dired-mountain-dir ()
  "Just a quick shortcut to my mounting folder"
  (interactive)
  (dired "~/Mountain"))

(defun fn/dired-fakespace-dir ()
  "Just a quick shortcut to my temp workspace folder"
  (interactive)
  (dired "~/Fakespace"))

(defun fn/dired-home-dir ()
  "Quickly visit home directory"
  (interactive)
  (dired "~"))

(defun fn/dired-lib-dir ()
  "Quickly visit library directory"
  (interactive)
  (dired fn/lib-dir))

(defun fn/dired-module-dir ()
  "Quickly visit modules library"
  (interactive)
  (dired "~/Modules"))

Performance Indicator

Check if Emacs is running fast.

(run-at-time nil 60 #'force-mode-line-update)

(when (require 'deferred)
  (defvar fn/-previous-time (current-time)
    "Previous time recording.  Not to be used directly.")

  (defvar fn/current-frame-delay 0.0
    "Fps recording.")

  (defun fn/record-frame-delay ()
    "Record frame delay."
    (prog1
        (let* ((now (current-time))
               (time-diff
                (float-time (time-subtract now fn/-previous-time))))
          (setq fn/current-frame-delay time-diff)
          (force-mode-line-update))
      (fn/-mark-time)))

  (defun fn/-mark-time ()
    "Mark the previous time for use"
    (setq fn/-previous-time (current-time)))

  (run-with-idle-timer 0 t #'fn/record-frame-delay)

  (add-hook 'pre-command-hook #'fn/-mark-time))

(unless (eq system-type 'windows-nt) ;; Windows requires powershell just to poll it. WTF!?
  (when (and (require 'deferred)
             (executable-find "grep")
             (executable-find "awk"))
    (defvar fn/current-cpu-usage 0.0
      "Cpu recording.")

    (defconst fn/cpu-usage-command "grep 'cpu ' /proc/stat | awk '{usage=($2+$4)*100/($2+$4+$5)} END {print usage}'")

    (defun fn/record-cpu-usage ()
      "Record cpu usage."
      (deferred:error
        (deferred:nextc
          (deferred:process-shell
            fn/cpu-usage-command)
          (lambda (value)
            (setq fn/current-cpu-usage (string-to-number value))))
        (lambda ()
          (setq fn/cpu-timer
                (cancel-timer fn/cpu-timer))

          (makunbound 'fn/current-cpu-usage)
          (message "Error with CPU monitor, shutting it down"))))

    (defvar fn/cpu-timer nil
      "A cpu timer if you ever want to quit it.")

    (defun fn/start-cpu-monitor ()
      "Start cpu monitor"
      (interactive)

      (when (timerp fn/cpu-timer)
        (cancel-timer fn/cpu-timer))

      (setq fn/cpu-timer
            (run-with-idle-timer 1 t #'fn/record-cpu-usage)))

    (fn/start-cpu-monitor))


  (when (and (require 'deferred)
             (executable-find "grep")
             (executable-find "awk")
             (executable-find "free"))
    (defvar fn/current-memory-usage 0.0
      "Memory recording.")

    (defconst fn/memory-usage-command
      "free | grep Mem | awk '{print $3/$2 * 100.0}'")

    (defun fn/record-memory-usage ()
      "Record memory usage."
      (deferred:error
        (deferred:nextc
          (deferred:process-shell
            fn/memory-usage-command)
          (lambda (value)
            (setq fn/current-memory-usage (string-to-number value))))
        (lambda ()
          (setq fn/memory-timer
                (cancel-timer fn/memory-timer))

          (makunbound 'fn/current-memory-usage)
          (message "Error with RAM monitor, shutting it down"))))

    (defvar fn/memory-timer nil
      "A memory timer.")

    (defun fn/start-memory-monitor ()
      "Start memory monitor."
      (interactive)
      (when (timerp fn/memory-timer)
        (cancel-timer fn/memory-timer))

      (setq fn/memory-timer
            (run-with-idle-timer 1 t #'fn/record-memory-usage)))

    (fn/start-memory-monitor))

  (when (and (require 'deferred)
             (executable-find "grep")
             (executable-find "awk")
             (executable-find "free"))
    (defvar fn/current-battery-usage 0.0
      "Battery usage.")

    (defconst fn/battery-usage-command "acpi -b | awk -F , '{print substr( $2, 1 , length($2) - 1) / 100}'"
      "Battery usage command.")

    (defun fn/record-battery-usage ()
      "Record battery usage."
      (deferred:error
        (deferred:nextc
          (deferred:process-shell
            fn/battery-usage-command)
          (lambda (value)
            (setq fn/current-battery-usage (string-to-number value))))
        (lambda ()
          (setq fn/battery-timer
                (cancel-timer fn/battery-timer))

          (makunbound 'fn/current-battery-usage)
          (message "Error with Battery monitor, shutting it down"))))

    (defvar fn/battery-timer nil
      "A battery timer.")

    (defun fn/start-battery-monitor ()
      "Start the battery monitor."
      (interactive)
      (when (timerp fn/battery-timer)
        (cancel-timer fn/battery-timer))

      (setq fn/battery-timer
            (run-with-idle-timer 1 t #'fn/record-battery-usage)))

    (fn/start-battery-monitor)))

Open buffers

This is just a convenience to open various buffers:

(defun fn/find-config-file ()
  (interactive)
  (find-file (expand-file-name "config.org" user-emacs-directory)))

(defun fn/open-message-buffer ()
  (interactive)
  (switch-to-buffer "*Messages*"))

Text-To-Speech

Some contrivances

(when (system-packages-ensure "espeak")
  (defcustom fn/espeak-executable "espeak"
    "The speech voicer.")

  (defcustom fn/espeak-args (list "-s" "140")
    "The speech voicer args.")

  (defun fn/espeak (text)
    "Speak up."
    (interactive "sWhat do you want to say? ")
    (apply #'start-process
           (append
            (list fn/espeak-executable nil fn/espeak-executable)
            fn/espeak-args
            (list text))))

  (defun fn/espeak-region ()
    "Speak up in the region."
    (interactive)
    (fn/espeak
     (buffer-substring-no-properties (region-beginning) (region-end)))))

Open in Sudo

Use tramp to open a file as root

(defun fn/sudo-edit (&optional arg)
  "Edit currently visited file as root.

With a prefix ARG prompt for a file to visit.
Will also prompt for a file to visit if current
buffer is not visiting a file."
  (interactive "P")
  (if (or arg (not buffer-file-name))
      (find-file (concat "/sudo:root@localhost:"
                         (ido-read-file-name "Find file(as root): ")))
    (find-alternate-file (concat "/sudo:root@localhost:" buffer-file-name))))

Clean Async Buffers

Just utilities to remove async buffers.

(defun fn/kill-inactive-async-buffers ()
  (interactive)
  (mapc (lambda (buf)
          (when (and (string-prefix-p "*Async Shell Command*" (buffer-name buf))
                     (not (get-buffer-process buf)))
            (kill-buffer buf)))
        (buffer-list)))

Key Bindings

Here is where all the key binding I mapped to

(defun fn/trigger-key (key)
  "Returns a function that triggers the function bound by the key"
  (let* ((key-trigger key)
      (trigger
       (lambda ()
         (interactive)
         (let ((key-command (key-binding key-trigger)))
           (cond
            (key-command
             (message "Triggering %s" key-command)
             (command-execute key-command))
            (t
             (message "No key bound to %s" key-trigger)))))))
    trigger))


;; Motion
(define-key fn-custom-prefix-map (kbd "b RET") (fn/trigger-key (kbd "C-RET")))
(define-key fn-custom-prefix-map (kbd "b b RET ") (fn/trigger-key (kbd "M-RET")))


;; Keys
(global-set-key (kbd "M-n") (fn/trigger-key (kbd "DEL")))
(global-set-key (kbd "C-c C-x")  (fn/trigger-key (kbd "M-x")))


;; Search
(define-key fn-standard-prefix-map (kbd "C-s") 'fn/isearch-forward-normally)
(define-key fn-standard-prefix-map (kbd "C-r") 'fn/isearch-backward-normally)


;; Buffers
(define-key fn-standard-prefix-map (kbd "k k") 'kill-this-buffer)
(define-key fn-standard-prefix-map (kbd "k C-k") 'fn/save-and-kill-buffer)
(define-key fn-standard-prefix-map (kbd "k &") 'fn/kill-inactive-async-buffers)

;; Windows
(define-key fn-custom-prefix-map (kbd "w h") 'fmw/four-part-horizontal-window-layout)
(define-key fn-custom-prefix-map (kbd "w v") 'fmw/four-part-vertical-window-layout)
(define-key fn-custom-prefix-map (kbd "w t") 'fmw/three-part-left-window-layout)
(define-key fn-custom-prefix-map (kbd "w 3") 'fmw/three-part-vertical-window-layout)
(define-key fn-custom-prefix-map (kbd "w 4") 'fmw/four-part-equal-vertical-window-layout)
(define-key fn-custom-prefix-map (kbd "w r") 'fmw/rotate-window-buffers)

(define-key fn-custom-prefix-map (kbd "w s") 'fn/swap-with-numbered-window)

;; Shell
(define-key fn-standard-prefix-map (kbd "x s") 'shell)


;; Customize
(define-key fn-standard-prefix-map (kbd "C g") 'customize-group)



;; Files
(define-key fn-custom-prefix-map (kbd "b c") 'fn/find-config-file)
(define-key fn-custom-prefix-map (kbd "b o") 'fn/find-main-org-file)
(define-key fn-custom-prefix-map (kbd "b l") 'fn/find-ledger-file)


;; Special Buffers
(define-key fn-custom-prefix-map (kbd "b m") 'fn/open-message-buffer)
(define-key fn-custom-prefix-map (kbd "b j") 'fn/display-jabber-roster-buffer)


;; Dired
(define-key fn-custom-prefix-map (kbd "d h") #'fn/dired-emacs-dir)
(define-key fn-custom-prefix-map (kbd "d d") #'fn/dired-download-dir)
(define-key fn-custom-prefix-map (kbd "d M") #'fn/dired-mountain-dir)
(define-key fn-custom-prefix-map (kbd "d f") #'fn/dired-fakespace-dir)
(define-key fn-custom-prefix-map (kbd "d ~") #'fn/dired-home-dir)
(define-key fn-custom-prefix-map (kbd "d l") #'fn/dired-lib-dir)
(define-key fn-custom-prefix-map (kbd "d m") #'fn/dired-module-dir)

(define-key fn-custom-prefix-map (kbd "d c") #'fn/checksum-current-directory)


;; Special
(define-key fn-standard-prefix-map (kbd "l l") #'fn/chat-log-open) ;; From binding above
(define-key fn-standard-prefix-map (kbd "l L") #'fn/chat-log-clear)
(define-key fn-standard-prefix-map (kbd "l a") #'fn/alert-log-open)


;; Espeak
(define-key fn-standard-prefix-map (kbd "M-s s") #'fn/espeak)

;; Functions
(define-key fn-custom-prefix-map (kbd  "r s") 'fn/startup)
(define-key fn-custom-prefix-map (kbd  "r c") 'fn/cleanup)

;; Custom Emacs
(define-key fn-custom-prefix-map (kbd  "e e") 'fn/bootstrap-experimental)
(define-key fn-custom-prefix-map (kbd  "e C-p") 'fn/bootstrap-previous)
(define-key fn-custom-prefix-map (kbd  "e s") 'fn/bootstrap-spacemacs)
(define-key fn-custom-prefix-map (kbd  "e f") 'fn/bootstrap-from-dir)

About

My emacs configuration redux

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages