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.
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
(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))
Do not byte compile a file.
(setq-local no-byte-compile t)
This makes it so that the file that is produced from tangling this file uses lexical scoping and all succeeding files
(setq lexical-binding t)
Add package sources if not present and reload, this should have been added in the init.el
but checked here again for completion.
(defconst fn/package-archives
`(("gnu" "http://elpa.gnu.org/packages/" 10)
("org" "http://orgmode.org/elpa/" 0 )
("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)
The package manager of this whole configuration
(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)
(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))))
Configuration for using the tangling nature of this configuration
(setq org-confirm-babel-evaluate nil
org-src-fontify-natively t
org-src-tab-acts-natively t)
Some constants to work with
(defconst fn/minute-per-second 60
"Obviously converting minutes to seconds")
(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")
(make-directory fn/lib-dir t)
(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.")
(make-directory fn/system-dir t)
(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.")
(make-directory fn/font-dir t)
(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.")
(make-directory fn/conf-dir t)
(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")
(make-directory fn/custom-module-dir t)
(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")
(make-directory fn/custom-script-dir t)
(add-to-list 'load-path fn/custom-script-dir)
Basic configurations anyone can do for vanilla Emacs
Basis for configuration
;; Don't really care about custom file
(setq custom-file (expand-file-name "custom-file.el" fn/cache-dir))
(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)
Everything related to the startup state
(defconst fn/gc-cons-threshold (* 256 1024 1024)
"A high limit for garbage colection.")
(setq gc-cons-threshold fn/gc-cons-threshold) ;; High memory for Emacs
(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)
;; Since the limit is high, it might never gc. So if I am idle, do some cleanup.
(run-with-idle-timer 30 t #'garbage-collect)
(setq inhibit-startup-screen t ;; No need for the awesome startup screen.
initial-scratch-message nil)
Some environment configuration.
(set-language-environment "UTF-8") ;; UTF-8 should be the enivorment
(setq visible-bell t) ;; Visual bell for me since audio is a bit more distractive
(fset 'yes-or-no-p 'y-or-n-p) ;; Y or N
Backups are good, just annoying when things are good
(defconst fn/backup-dir-name "backups/"
"Backup directory name")
(defconst fn/backup-dir (expand-file-name fn/backup-dir-name fn/cache-dir)
"Backup directory")
(defconst fn/auto-save-dir-name "auto-save-list/"
"Auto save directory name")
(defconst fn/auto-save-dir (expand-file-name fn/auto-save-dir-name 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)))
Save minibuffer history
(defconst fn/savehist-file-name "savehist"
"Save history file name")
(defconst fn/savehist-file (expand-file-name fn/savehist-file-name fn/cache-dir)
"Save history file")
(setq savehist-file fn/savehist-file
history-length t
history-delete-duplicates t
savehist-save-minibuffer-history t
savehist-additional-variables (list 'kill-ring
'search-ring
'regexp-search-ring))
(savehist-mode t)
(setq bookmark-file (expand-file-name "bookmarks" fn/cache-dir)
bookmark-default-file (expand-file-name "bookmarks-default" fn/cache-dir))
(add-to-list
'display-buffer-alist
(cons
(rx bos "*Bookmark List*" eos)
(cons 'display-buffer-same-window (list))))
Some editing configurations
(setq whitespace-line-column 10000 ;; No line too long font locking please
;; C-M-a should go to the beginning of a sentence
sentence-end-double-space nil
require-final-newline t)
;; I like tabs but they should just be converted to spaces for equality
(setq-default indent-tabs-mode nil)
(add-hook 'makefile-mode 'indent-tabs-mode)
Bookmark configuration
(setq bookmark-default-file (expand-file-name "bookmarks" fn/cache-dir)
bookmark-save-flag t)
You must have this setup, it’s like butter and you’re the bread.
(defconst fn/default-search-whitespace-regexp search-whitespace-regexp
"Store the default whitespace option")
(setq search-highlight t)
;; This ignores whitespace when searching
(setq-default search-whitespace-regexp ".*?")
(defun fn/isearch-forward-normally ()
"This custom command does i-search without the whitespace skips,
the vanilla behavior"
(interactive)
(let ((search-whitespace-regexp fn/default-search-whitespace-regexp))
(isearch-forward)))
(defun fn/isearch-backward-normally ()
"Ditto with fn/isearch-foward-normally except backwards."
(interactive)
(let ((search-whitespace-regexp fn/default-search-whitespace-regexp))
(isearch-backward)))
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)
(make-variable-buffer-local
(defvar fn/whitespace-cleanup-on-save t
"When non-nil, delete trailing whitespace on save"))
(add-to-list 'safe-local-variable-values '(fn/whitespace-cleanup-on-save))
(defun fn/delete-trailing-whitespace-on-save ()
"Delete trailing whitespace on save."
(when fn/whitespace-cleanup-on-save
(with-current-buffer (current-buffer)
(message "Deleting trailing whitespace in %s" (current-buffer))
(delete-trailing-whitespace (point-min) (point-max)))))
;; (add-hook 'before-save-hook 'fn/delete-trailing-whitespace-on-save)
(defun fn/disable-delete-trailing-whitespace-on-save ()
"Disable `fn/delete-trailing-whitespace-on-save' for a specific mode."
(setq-local fn/whitespace-cleanup-on-save nil))
(add-hook 'makefile-mode-hook 'fn/disable-delete-trailing-whitespace-on-save t)
(temp-buffer-resize-mode t)
(setq compilation-window-height 10
compilation-scroll-output 'first-error
compilation-ask-about-save nil)
I better be a cat
(mouse-avoidance-mode 'cat-and-mouse)
Some minor shell enhancement
(setq comint-input-ignoredups t)
Some basic configuration with url
.
(with-eval-after-load 'url
(setq url-configuration-directory
(expand-file-name "url" fn/cache-dir)))
Some other configurations that I can’t classify yet
(setq auth-sources (list (list :source "~/.authinfo.gpg")))
(setq browse-url-browser-function 'browse-url-generic
browse-url-generic-program "chromium")
Unlock some commands I need
(put 'narrow-to-region 'disabled nil)
(put 'downcase-region 'disabled nil)
(put 'upcase-region 'disabled nil)
Allow safe local variables
(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))
Everything needed to support this more complex configurations
Functionally elisp
(use-package dash
:ensure t
:defer t)
(use-package dash-functional
:ensure t
:after dash)
Not really builtin but should be
(use-package async
:ensure t
:defer t)
A string library, everyone needs that
(use-package s
:ensure t)
File manipulation library for Emacs
(use-package f
:ensure t)
Something to help with package configuration
(defconst fn/promise-package-dir (expand-file-name "promise/" fn/custom-module-dir)
"Promise package location")
(use-package promise
:load-path fn/promise-package-dir
:demand t
:config
(defmacro fn/use-function (name function &rest body)
(let ((function-name-var (make-symbol "function-name")))
`(let ((,function-name-var ,(symbol-name name)))
(message "Checking function %s" ,function-name-var)
(if (not (fboundp (quote ,function)))
(message "Function does not exist")
(message "Loading function %s" ,function-name-var)
,@body
(message "Configured function %s" ,function-name-var)))))
(put 'fn/use-function 'lisp-indent-function 'defun)
(defmacro fn/use-feature (name features &rest body)
"The equivalent for `use-package' but for combining features as promises."
(let ((now-var (make-symbol "now"))
(elapsed-var (make-symbol "elapsed"))
(feature-name-var (make-symbol "feature-name")))
`(promise-then
(apply #'promise-all-features (list ,@(if features
(mapcar (lambda (feature) (list 'quote feature)) features)
(list (list 'quote 'use-package)))))
(lambda (loaded-features)
(let ((,feature-name-var ,(symbol-name name))
(,now-var (current-time)))
(message "Loading feature package %s" ,feature-name-var)
,@body
(let ((,elapsed-var (float-time (time-subtract (current-time) ,now-var))))
(if (> ,elapsed-var ,use-package-minimum-reported-time)
(message
"Configuring feature package %s ... done (%.3fs)"
,feature-name-var
,elapsed-var)
(message
"Configuring feature package %s... done"
,feature-name-var))
nil))))))
(put 'fn/use-feature 'lisp-indent-function 'defun)
)
Unicode is an required aesthetic
(use-package pcache ;; Required by unicode-fonts
:ensure t
:init
;; Mentioned here to redirect directory
(setq pcache-directory (expand-file-name "pcache/" fn/cache-dir)))
(use-package unicode-fonts
:ensure t
:demand t
:config
(unicode-fonts-setup))
Nice to have some persistence.
(use-package persistent-soft
:ensure t
:demand t
:config
(defconst fn/persistence-location "custom-persistence"
"My persistence location.")
(defvar fn/persistence-managed-symbols (list)
"Managed persistent symbols.")
(defun fn/persistence-store (key value)
"Just a wrapper for `persistent-soft-store' with KEY and VALUE,
location is defined by `fn/-persistence-location'."
(persistent-soft-store key value fn/persistence-location))
(defun fn/persistence-fetch (key)
"Another wrapper for `persistent-soft-fetch' with KEY,
and location is defined by `fn/custom-persistence-file'."
(persistent-soft-fetch key fn/persistence-location))
(defun fn/persistence-flush ()
"Store all symbols in `fn/persistence-managed-symbols' before Emacs closes."
(mapc
(lambda (symbol)
(ignore-errors
(fn/persistence-store symbol (symbol-value symbol))))
fn/persistence-managed-symbols))
(add-hook 'kill-emacs-hook #'fn/persistence-flush)
(defun fn/persistence-manage-symbol (symbol)
(set symbol (fn/persistence-fetch symbol))
(push symbol fn/persistence-managed-symbols)))
The path variable from the shell is need to run commands
(defconst fn/exec-path-from-shell-package-dir
(expand-file-name "exec-path-from-shell/" fn/custom-module-dir)
"Exec package dir")
(use-package exec-path-from-shell
:ensure t
:load-path fn/exec-path-from-shell-package-dir
:demand t
:if (not (eq system-type 'windows-nt)) ;; Sorry Windows
:config
(setq exec-path-from-shell-variables
(list
"PATH" "MANPATH"
"PROMPT" "PS1"
"JAVA_HOME" "ECLIPSE_HOME")
exec-path-from-shell-arguments
(list
"-l" "-i"))
(exec-path-from-shell-initialize))
My custom library for exporting.
(defconst fn/htmlize-package-dir
(expand-file-name "htmlize/" fn/custom-module-dir)
"Htmlize package dir")
(use-package htmlize
:ensure t
:defer t)
Nice logging library
(unless noninteractive
(use-package log4e
:ensure t
:demand t
:config
(log4e:deflogger
"chat"
">>> %t \n%m\n<<<"
"%F %H:%M:%S"
(list (cons 'info "info")))
(chat--log-enable-logging)
(defface fn/chat-server-name '((t (:weight bold :height 0.9)))
"Chat server name face")
(defface fn/chat-channel-name '((t (:weight ultra-bold :height 1.1)))
"Chat server name face")
(defface fn/chat-chatter-name '((t (:underline t :weight semi-bold :height 1.0)))
"Chat sender name face")
(defface fn/chat-message '((t (:weight thin :height 1.0)))
"Chat message face")
(defun fn/chat-propertize (text style)
"Add extra coloring with TEXT and STYLE."
(let ((new-text (substring text)))
(add-face-text-property
0
(length new-text)
style
nil
new-text)
new-text))
(defun fn/prettify-word (word)
"Prettify word if possible."
(if (fboundp 'text-candy-candied-word)
(text-candy-candied-word word)
word))
(defun fn/chat-log (server channel chatter message)
"Log SERVER, CHANNEL, CHATTER and MESSAGE."
(unless (string-empty-p message)
(let* ((out
(string-trim-right
(format "[%s/%s] %s: %s"
(fn/chat-propertize (fn/prettify-word server) 'fn/chat-server-name)
(fn/chat-propertize (fn/prettify-word channel) 'fn/chat-channel-name)
(fn/chat-propertize chatter 'fn/chat-chatter-name)
(fn/chat-propertize message 'fn/chat-message)))))
(chat--log-info out)
out)))
(defun fn/chat-log-open ()
"Open chat log."
(interactive)
(chat--log-open-log))
(defconst fn/chat-log-file (expand-file-name ".chat-log.txt.gpg" fn/cache-dir)
"My chat log file.")
(defun fn/chat-log-clear ()
"Clear chat log."
(interactive)
(chat--log-clear-log))
(defun fn/chat-log-flush (&optional retain)
"Save log for posterity. If RETAIN is non-nil, do not clear the log."
(ignore-errors
(save-window-excursion
(fn/chat-log-open) ;; NOTE: Error might result if the log isn't populated yet.
(let ((new-log
(with-current-buffer log4e--log-buffer-chat
(buffer-substring-no-properties (point-min) (point-max))))
(file (find-file-noselect fn/chat-log-file)))
(with-current-buffer file
(end-of-buffer)
(newline)
(insert new-log)
(save-buffer))
(unless retain
(fn/chat-log-clear))
nil))))
(defun fn/chat-log-flush-before-clear (&rest args)
"Save the log before clearing."
(fn/chat-log-flush t))
;; (advice-add 'chat--log-clear-log :before #'fn/chat-log-flush-before-clear)
;; (add-hook 'kill-emacs-hook #'fn/chat-log-flush)
(fn/use-feature log4e-mode--mode-icon
(all-the-icons)
(fn/add-major-mode-icon
'log4e-mode
(list 'all-the-icons-octicon "clippy" :v-adjust 0.0)))))
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))
Making Emacs more modern.
Sadly, this has to be placed here because fn/use-feature
cannot load
it properly.
(defconst fn/all-the-icon-font-dir (expand-file-name "all-the-icons-font" fn/custom-module-dir)
"All the icon font library.")
(defconst fn/all-the-icon-font-families
(list
(cons "FontAwesome" "fontawesome.ttf")
(cons "Weather Icons" "weathericons.ttf")
(cons "file-icons" "file-icons.ttf")
(cons "github-octicons" "octicons.ttf")
(cons "all-the-icons" "all-the-icons.ttf"))
"The font available.")
(fset 'fn/add-major-mode-icon 'ignore)
(unless noninteractive
(use-package all-the-icons
:ensure t
:demand t
: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))
(defun fn/check-installed-font-families ()
"Check if `fn/all-the-icon-font-families' is intalled."
(mapc
(lambda (pair)
(pcase-let ((`(,family . ,_) pair))
(unless (member family (font-family-list))
(message "The %s is not installed, you might want it installed." family))))
fn/all-the-icon-font-families))
(defun fn/install-font-families-locally ()
"Install all-the-icons 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)
(mapcar
(lambda (font-file)
(copy-file font-file local-font-folder t))
(directory-files fn/all-the-icon-font-dir t ".ttf"))
(when (executable-find "fc-cache")
(shell-command "fc-cache"))))
(add-hook 'after-init-hook 'fn/check-installed-font-families)))
Dependencies that aren’t really required
(use-package org-jekyll-blogger
:load-path fn/custom-script-dir
:defer t)
(use-package prodigy-set
:load-path fn/custom-script-dir
:defer t)
(use-package text-candy
:load-path fn/custom-script-dir
:defer 5)
(use-package xpm
:load-path fn/custom-script-dir
:defer t)
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.")
(defun fn/record-executable (orig-fun executable)
"Record executable is missing."
(lexical-let ((result (funcall orig-fun executable)))
(prog1
result
(unless result
(add-to-list 'fn/missing-executables executable)))))
(advice-add 'executable-find :around #'fn/record-executable)
(defun fn/notify-missing-executables ()
"Notify executables that are missing through `fn/missing-executables'."
(when fn/missing-executables
(message "You're missing some executables: %s" fn/missing-executables)))
(add-hook 'after-init-hook #'fn/notify-missing-executables t)
(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)
(fn/use-executables bootstrap-check
(emacs git tmux)
nil)
(fn/use-executables downloader-check
(wget curl rsync)
nil)
(fn/use-executables secure-check
(tor polipo)
nil)
(fn/use-executables browser-check
(w3m firefox chromium)
nil)
Shell related aspect
Some configurations on the terminal
(setq async-shell-command-buffer 'new-buffer)
(fn/use-feature shell-mode--mode-icon
(all-the-icons)
(fn/add-major-mode-icon
'shell-mode
(list 'all-the-icons-faicon "terminal" :v-adjust 0.0)))
More things for sh-mode
(use-package company-shell
:ensure t
:after (shell company)
:hook (sh-mode . company-mode)
:config
(add-to-list 'company-backends 'company-shell))
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))))
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))
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)
Env per directory. (Want to hack this with Emacs)
(fn/use-executables direnv-check
(direnv)
(use-package direnv
:ensure t
:unless noninteractive
:demand t
:config
(direnv-mode)))
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))
:defer t
: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)
(fn/use-feature prodigy-mode--mode-icon
(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))))
Ensure when Emacs closes, kill all processes
(fn/use-feature prodigy-auto-kill
(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))
(lexical-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))
A dependency to customize prodigy, giving the command fn/prodigy-define-service
.
(fn/use-feature prodigy-custom
(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."
(lexical-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
(lexical-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."
(lexical-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."
(lexical-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
(lexical-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))
Some commands that can be refactored out
(fn/use-feature prodigy-command-tags
(prodigy)
(fn/use-executables prodigy-polipo
(polipo)
(prodigy-define-tag
:name 'polipo
:cwd user-emacs-directory
:command "polipo"
:stop-signal 'kill
:kill-process-buffer-on-stop t
:ready-message "\\.*Established listening socket on \\.*")
(defun fn/prodigy-create-polipo-conf (file options)
"Update FILE with polipo OPTIONS."
(with-temp-file file
(insert
(string-join
(mapcar
(lambda (pair)
(pcase-let ((`(,key . ,value) pair))
(format
"%s = %s"
key
(typecase value
(symbolp (symbol-name value))
(numberp (number-to-string value))
(stringp (format "\"%s\"" value))))))
options)
"\n")))))
(fn/use-executables prodigy-tor
(tor)
(prodigy-define-tag
:name 'tor
:cwd user-emacs-directory
:command "tor"
:stop-signal 'kill
:kill-process-buffer-on-stop t
:ready-message ".*Bootstapped 100.*")
(defun fn/prodigy-create-tor-conf (file options)
"Update FILE with tor OPTIONS."
(with-temp-file file
(insert
(string-join
(mapcar
(lambda (pair)
(pcase-let ((`(,key . ,value) pair))
(format
"%s %s"
key
(typecase value
(symbolp (symbol-name value))
(numberp (number-to-string value))
(stringp value)))))
options)
"\n")))))
(fn/use-executables prodigy-privoxy
(privoxy)
(prodigy-define-tag
:name 'privoxy
:cwd user-emacs-directory
:command "privoxy"
:stop-signal 'kill
:kill-process-buffer-on-stop t
:ready-message "\\.*Listening on port \\.*")
(defun fn/prodigy-create-privoxy-conf (file options)
"Update FILE with privoxy OPTIONS."
(with-temp-file file
(insert
(string-join
(mapcar
(lambda (pair)
(pcase-let ((`(,key . ,value) pair))
(format
"%s %s"
key
(typecase value
(symbolp (symbol-name value))
(numberp (number-to-string value))
(stringp value)))))
options)
"\n")))))
(fn/use-executables prodigy-jekyll
(jekyll)
(prodigy-define-tag
:name 'jekyll
:command "jekyll"
:args (list "serve")
:stop-signal 'kill
:kill-process-buffer-on-stop t))
(fn/use-executables prodigy-npm
(npm)
(prodigy-define-tag
:name 'npm
:command "npm"
:args `("run start")
:stop-signal 'kill
:kill-process-buffer-on-stop t))
(fn/use-executables prodigy-offlineimap
(offlineimap)
(defcustom fn/prodigy-offlineimap-state-change-hook (list)
"`offlineimap' state change hook.
Possible values are `error', `synced' and `ready'.")
(defvar fn/prodigy-offlineimap-current-state nil
"The state of parsing `offlineimap'. Not to be used directly.")
(defun fn/prodigy-offlineimap-state-change-listener (&rest args)
"Just notify me when the offlineimap has something new"
(lexical-let ((output (plist-get args :output))
(service (plist-get args :service)))
(pcase output
((pred (string-match-p "OfflineIMAP"))
(prodigy-set-status service 'ready)
(run-hook-with-args 'fn/prodigy-offlineimap-state-change-hook service 'ready))
((and (guard (not fn/prodigy-offlineimap-current-state))
(pred (string-match-p "Copy message")))
(setq fn/prodigy-offlineimap-current-state t))
((and (guard fn/prodigy-offlineimap-current-state)
(pred (string-match-p "Finished account")))
(run-hook-with-args 'fn/prodigy-offlineimap-state-change-hook service 'synced)
(setq fn/prodigy-offlineimap-current-state nil)))))
(prodigy-define-tag
:name 'offlineimap
:command "offlineimap"
:on-output 'fn/prodigy-offlineimap-state-change-listener))
(fn/use-executables prodigy-mbsync
(mbsync)
(defconst fn/mbsync-daemon-command "mbsync-daemon"
"Wrappd `mbsync' as a daemon.")
(defconst fn/mbsync-slave-pattern
(rx line-start
"slave: "
(group-n 1 (one-or-more digit))
" messages, "
(group-n 2 (one-or-more digit))
" recent" line-end))
(defcustom fn/prodigy-mbsync-state-change-hook (list)
"`mbsync' state change hook.
Possible values are `error', `synced' and `ready'.")
(defun fn/prodigy-mbsync-state-change-listener (&rest args)
"Listen on `mbsync' output for state changes."
(lexical-let ((output (plist-get args :output))
(service (plist-get args :service)))
(pcase output
((pred (string-prefix-p "Starting daemon"))
(prodigy-set-status service 'ready)
(run-hook-with-args 'fn/prodigy-mbsync-state-change-hook service 'ready))
((pred (string-prefix-p "Error:"))
(run-hook-with-args 'fn/prodigy-mbsync-state-change-hook service 'error))
((pred (string-match fn/mbsync-slave-pattern))
(let ((recent (match-string 2 output)))
(when (> (string-to-number recent) 0)
(run-hook-with-args 'fn/prodigy-mbsync-state-change-hook service 'synced)))))))
(prodigy-define-tag
:name 'mbsync
:command fn/mbsync-daemon-command
:args `("-V" "-a")
:on-output 'fn/prodigy-mbsync-state-change-listener))
(fn/use-executables prodigy-mpd
(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"))))))
Services are grouped together, starting and stopping together or in sequence.
(use-package prodigy-set
:load-path fn/custom-script-dir
:after prodigy
:config
nil)
Similarly, manage processes.
(use-package proced
:ensure t
:defer t
:bind (:map fn-standard-prefix-map
("C-M-p p" . proced)))
Work better with tmux
(fn/use-executables emamux-check
(tmux)
(use-package emamux
:ensure t
:defer 1
:config
(shell-command "tmux -2 -C new -s emamux")))
Emacs and term
(use-package multi-term
:ensure t
:defer t)
Containers eh?
(fn/use-executables docker-check
(docker)
(use-package docker
:ensure t
:defer t
:commands (docker-images docker-containers docker-volumes docker-networks docker-machines)
:config
nil))
Something to edit Dockerfile
(use-package dockerfile-mode
:ensure t
:mode (("Dockerfile\\'" . dockerfile-mode)))
Vagrant tools
(use-package vagrant
:ensure t
:defer t
:config
nil)
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)))
Anything to do with editing in Emacs
Visual lines make more sense than the hard lines, what you see is what you get. Besides, I use syntax motions
(fn/use-feature visual-line-mode
nil
(global-visual-line-mode t)
(diminish 'visual-line-mode))
Useful mode when writing, keeps things under 80 characters.
(fn/use-feature auto-fill-mode
nil
(add-hook 'text-mode-hook 'turn-on-auto-fill)
(diminish 'auto-fill-function)
(setq-default fill-column 72))
The more generic revert
(fn/use-feature auto-revert
nil
(global-auto-revert-mode t)
(setq global-auto-revert-non-file-buffers t
auto-revert-verbose nil)
(diminish 'auto-revert-mode))
Enhancement for log reading
(use-package autorevert
:diminish auto-revert-mode
:mode (("\\.log\\'" . auto-revert-tail-mode)));
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)))
Another great feature for marking
(use-package expand-region
:ensure t
:bind (("C-=" . er/expand-region)))
This functions does not get too much attention
(use-package multiple-cursors
:ensure t
:bind (("C-S-c C-S-c" . mc/edit-lines)
("C->" . mc/mark-next-like-this)
("C-<" . mc/mark-previous-like-this)
("C-c C-<" . mc/mark-all-like-this)))
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)))
I don’t like the camelCase convention so I make it more like Emacs.
(unless noninteractive
(with-eval-after-load 'glasses
(setq glasses-separator "~")))
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))
(fn/use-feature hippie-ggtags
(ggtags)
(add-to-list 'hippie-expand-try-functions-list 'ggtags-try-complete-tag)))
A replacement for autocomplete
.
(use-package company
:ensure t
: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 3
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)))
Snippet system along with autocomplete is awesome
(use-package yasnippet
:ensure t
:unless noninteractive
:diminish yas-minor-mode
:disabled t
:bind (:map fn-standard-prefix-map
("y" . yas-new-snippet))
:hook (prog-mode . yas-minor-mode)
:init
(setq-default yas-snippet-dirs
`(,(expand-file-name "snippets" fn/extra-dir)))
:config
(setq yas/root-directory (expand-file-name "snippets" fn/extra-dir)
yas--default-user-snippets-dir yas/root-directory
yas-snippet-dirs `(,yas/root-directory))
(make-directory yas--default-user-snippets-dir t)
(setq yas-verbosity 3)
(push 'yas-hippie-try-expand hippie-expand-try-functions-list)
(defun fn/add-company-yasnippet ()
"Add yasnippet to company backends"
(fn/append-to-backends 'company-yasnippet))
(add-hook 'after-init-hook 'fn/add-company-yasnippet)
(yas-reload-all))
Things to assist in managing Emacs
Things to jump around with
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))
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))
Anything related to window management
Managing buffers the Emacs way.
(defconst fn/desktop-map (fn/make-prefixed-keymap (kbd "d") fn-standard-prefix-map)
"My custom desktop bookmarked map.")
(use-package desktop
:defer t
:config
(defconst fn/desktop-dir (expand-file-name "desktop/" fn/cache-dir)
"My desktop dir")
(make-directory fn/desktop-dir t)
(setq desktop-path (list fn/desktop-dir)
desktop-dirname fn/desktop-dir
desktop-base-file-name "blank"
desktop-base-lock-name ".desk-lock"
desktop-save 'if-exists
desktop-restore-eager 15
desktop-lazy-idle-delay 10
desktop-lazy-verbose t)
(desktop-save-mode)
(fn/use-feature bookmarked-desktop
(bookmark+)
(define-key fn/desktop-map (kbd "s") 'bmkp-set-desktop-bookmark)
(setq bmkp-desktop-default-directory fn/desktop-dir
bmkp-desktop-jump-save-before-flag t)))
(use-package desktop+
:ensure t
:after desktop
:bind (:map fn/desktop-map
("s" . desktop+-create)
("l" . desktop+-load))
:config
(setq desktop+-base-dir fn/desktop-dir))
Saving window state is very helpful specially when you move a lots
(use-package workgroups2
:ensure t
:defer t
:bind (("C-c w w" . workgroups-mode))
:init
(setq wg-prefix-key (kbd "C-c w")
wg-session-file (expand-file-name "workgroups" fn/cache-dir))
(add-hook 'after-init-hook #'workgroups-mode)
:config
(setq wg-morph-on nil
wg-mode-line-display-on nil
wg-mess-with-buffer-list nil
wg-buffer-auto-association-on nil
wg-undoify-window-configuration-change nil
wg-buffer-local-variables-alist (list)
wg-restore-frame-position nil
wg-restore-scroll-bars nil
wg-restore-margins nil
wg-restore-point t
wg-restore-point-max t
wg-restore-mark nil
wg-restore-window-dedicated-p nil
wg-remember-frame-for-each-wg nil
wg-load-last-workgroup nil
wg-emacs-exit-save-behavior 'save
wg-workgroups-mode-exit-save-behavior 'save)
(defun fn/wg-session-list-buffers (&optional frame)
"Get the current session buffers."
(-reject
(-compose
(-partial #'s-starts-with-p " ")
#'buffer-name)
(funcall wg-buffer-list-function frame)))
(defun fn/list-to-hash-table (xs)
"Convert XS to a hash table with equality comparison."
(let ((table (make-hash-table)))
(mapc (lambda (x) (puthash x t table)) xs)
table))
(defun fn/wg-switch-to-buffer ()
"Switch buffer with my custom workgroup."
(interactive)
(lexical-let* ((session-buffers (fn/wg-session-list-buffers))
(buffer-table (fn/list-to-hash-table session-buffers))
(predicate
(lambda (buffer-pair)
(when (consp buffer-pair)
(lexical-let* ((buffer (cdr buffer-pair)))
(gethash buffer buffer-table nil)))))
(buffer (read-buffer
"Switch to session buffer: "
nil
t
predicate)))
(switch-to-buffer buffer)))
(defun fn/wg-create-workgroup ()
"Create a blank workgroup."
(interactive)
(let ((current-prefix-arg t))
(call-interactively #'wg-create-workgroup)))
(define-key wg-prefixed-map (kbd "b") #'fn/wg-switch-to-buffer)
(define-key wg-prefixed-map (kbd "s") #'wg-switch-to-workgroup)
(run-with-idle-timer 0 nil (lambda ()
(define-key workgroups-mode-map
(vector 'remap 'wg-create-workgroup)
#'fn/wg-create-workgroup)))
(defun fn/wg-cleanup ()
"Some cleanup with workgroups since it becomes really slow speciall with `execute-extended-command' with `smex'."
(wg-perform-session-maintenance))
(add-hook 'wg-after-switch-to-workgroup-hook 'fn/wg-cleanup)
(add-hook 'wg-after-switch-to-workgroup-hook 'garbage-collect)
;; Performance
;; Limit tracked buffers
(defcustom fn/wg-buf-list-limit 500
"The maximal number of buffers that should tracked if `fn/wg-limit-add-buffer-to-buf-list' is enabled.")
(defun fn/wg-limit-add-buffer-to-buf-list (&rest args)
"Limit the buffers tracked by `wg-add-buffer-to-buf-list' based on `fn/wg-buf-list-limit'."
(when (fboundp 'wg-buf-list)
(when (>= (length (wg-buf-list)) fn/wg-buf-list-limit)
(nbutlast (wg-buf-list)))))
(advice-add 'wg-add-buffer-to-buf-list :after #'fn/wg-limit-add-buffer-to-buf-list)
;; Golden Ratio fix
(fn/use-feature workgroups-golden-ratio
(workgroups2 golden-ratio)
(defun fn/workgroups-switch-disable-golden-ratio (orig-fun &rest args)
(if (not golden-ratio-mode)
(apply orig-fun args)
(prog2
(golden-ratio-mode -1)
(apply orig-fun args)
(golden-ratio-mode +1))))
(advice-add 'wg-switch-to-workgroup :around #'fn/workgroups-switch-disable-golden-ratio))
;; Multi frame fix
(defun fn/wg-delete-other-frames (&rest args)
"Remove other frames on exit since it causes a `PickelError'."
(delete-other-frames))
(advice-add 'wg-save-session-on-exit :before #'fn/wg-delete-other-frames))
Hooks for each workgroup
(use-package workgroups-layout
:load-path fn/custom-script-dir
:defer t
:after (workgroups2)
:commands (workgroups-layout-mode)
:config
(workgroups-layout-mode)
(defvar fn/workgroups-layout-icons (list)
"An list of workgroup config icons, taken from `all-the-icons' mode line.")
(defun fn/workgroups-layout-define-workgroup-config-icon (workgroup icon-family icon-function icon-name &rest args)
"Just like with `all-the-icons' mode line, this is for workgroup config with
ICON-FAMILY, ICON-FUNCTION, ICON-NAME and ARGS."
(let ((workgroup-name (wg-workgroup-name workgroup)))
(add-to-list
'fn/workgroups-layout-icons
(append
(list
workgroup-name
icon-family
icon-function
icon-name)
args))))
(defun fn/workgroups-layout-workgroup-icon ()
"Return icon for workgroup as defined for `fn/workgroups-layout-icons' list"
(-if-let* ((current-workgroup-name (workgroups-layout-current-workgroup-name))
(result (assoc current-workgroup-name fn/workgroups-layout-icons)))
(let* ((icon-result (cdr result))
(icon-font-function (car icon-result))
(icon-function (cadr icon-result))
(icon-args (cddr icon-result)))
(propertize
(format " %s " (apply icon-function icon-args))
'help-echo (format "Workgroup: `%s`" current-workgroup-name)
'face
(list
:family (funcall icon-font-function))))
nil)))
Specific layouts for Emacs
(use-package e2wm
:ensure t
:defer t
:config
(fn/use-feature e2em-disable-golden-ratio
(e2wm golden-ratio)
(defun fn/e2wm-disable-golden-ratio ()
"Disable `golden-ratio-mode' when layout is managed."
(golden-ratio-mode -1))
(add-hook 'e2wm:pre-start-hook #'fn/e2wm-disable-golden-ratio)))
You got to have those window configuration
(unless noninteractive
(use-package winner
:ensure t
:config
(winner-mode t)))
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))))))
Making window layouts
(use-package window-layout
:ensure t
:config
(fn/use-feature window-numbered-layout
(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
(lexical-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)))
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)))
Nicer naming convention
(unless noninteractive
(use-package uniquify
:if (version<= "24.3.1" emacs-version)
:demand t
:config
(setq uniquify-buffer-name-style 'post-forward-angle-brackets)))
Anything related to buffers
Must have a project finder when using a project.
(use-package projectile
:ensure t
:defer 1
: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
(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"))
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))))
;; Safety Loader
(defvar fn/checked-projects (list)
"Projects that are trusted with loading `fn/project-init-files' or not.")
(defvar fn/loaded-projects (list)
"Projects that have been loaded by `fn/load-project-file'.")
(unless (fboundp 'file-attribute-modification-time)
(defun file-attribute-modification-time (attributes)
"The last access time of ATTRIBUTES"
(nth 5 attributes)))
(defun fn/trust-project ()
"Trust current project."
(interactive)
(if (not (projectile-project-p))
(message ("Not in a project."))
(let* ((project-root (projectile-project-root))
(project-entry (assoc project-root fn/checked-projects)))
(if project-entry
(progn
(setcdr
project-entry
(plist-put
(cdr project-entry)
:trusted
t)))
(add-to-list
'fn/checked-projects
(list project-root
:last-modification-time (file-attribute-modification-time (file-attributes project-root))
:project-root project-root
:trusted t)))
(message "Project %s trusted" project-root))))
(defun fn/safe-project-p (project-root)
"Check if PROJECT-ROOT can be trusted."
(let* ((last-modification-time
(file-attribute-modification-time (file-attributes project-root)))
(safe-project-properties
(or (cdr (assoc project-root fn/checked-projects))
(let* ((base-properties
(list :last-modification-time last-modification-time
:project-root project-root))
(trusted
(yes-or-no-p
(format "Do you ultimately trust the project at %s?" project-root)))
(new-properties
(plist-put base-properties :trusted trusted)))
new-properties))))
(prog1
(plist-get safe-project-properties :trusted)
(add-to-list 'fn/checked-projects
(cons project-root safe-project-properties)))))
(defun fn/safe-load-project-file ()
"Safety wrapper around `fn/load-project-file' with `fn/safe-project-p'."
(if (not (projectile-project-p))
(message "Not in a project.")
(let ((project-root (projectile-project-root))
(project-name (projectile-project-name)))
(when (not (member project-root fn/loaded-projects))
(if (not (fn/safe-project-p project-root))
(message "Project script for %s is not trusted." project-name)
(fn/load-project-file)
(add-to-list 'fn/loaded-projects project-root))))))
(defun fn/safe-load-project-local-file ()
"Safety wrapper around `fn/load-project-local-file' with `fn/safe-project-p'."
(if (not (projectile-project-p))
(message "Not in a project.")
(let ((project-root (projectile-project-root))
(project-name (projectile-project-name)))
(if (not (fn/safe-project-p project-root))
(message "Project local script for %s is not trusted." project-name)
(fn/load-project-local-file)))))
;; Project root script should run before project file script
(add-hook 'find-file-hook #'fn/safe-load-project-file t)
(add-hook 'find-dired-hook #'fn/safe-load-project-file t)
(add-hook 'dired-mode-hook #'fn/safe-load-project-file t)
(add-hook 'find-file-hook #'fn/safe-load-project-local-file t)
(add-hook 'find-dired-hook #'fn/safe-load-project-local-file t)
(add-hook 'dired-mode-hook #'fn/safe-load-project-local-file t)
(when (require 'savehist)
(add-to-list 'savehist-additional-variables 'fn/checked-projects)))
Fix for the completion system being removed each time an upgrade is completed.
(with-eval-after-load 'projectile
(cl-defun projectile-completing-read (prompt choices &key initial-input action)
"Present a project tailored PROMPT with CHOICES."
(let ((prompt (projectile-prepend-project-name prompt))
res)
(setq res
(cond
((eq projectile-completion-system 'ido)
(ido-completing-read prompt choices nil nil initial-input))
((eq projectile-completion-system 'default)
(completing-read prompt choices nil nil initial-input))
((eq projectile-completion-system 'helm)
(if (and (fboundp 'helm)
(fboundp 'helm-make-source))
(helm :sources
(helm-make-source "Projectile" 'helm-source-sync
:candidates choices
:action (if action
(prog1 action
(setq action nil))
#'identity))
:prompt prompt
:input initial-input
:buffer "*helm-projectile*")
(user-error "Please install helm from \
https://github.com/emacs-helm/helm")))
((eq projectile-completion-system 'ivy)
(if (fboundp 'ivy-read)
(ivy-read prompt choices
:initial-input initial-input
:action (prog1 action
(setq action nil))
:caller 'projectile-completing-read)
(user-error "Please install ivy from \
https://github.com/abo-abo/swiper")))
(t (funcall projectile-completion-system prompt choices))))
(if action
(funcall action res)
res))))
The revolutionary package to find
(use-package helm
:ensure t
:unless noninteractive
:defer 3
: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 'helm-config)
(helm-mode t)
;; core & utils
(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-debug-root-directory fn/cache-dir)
;; files & command
(setq helm-ff-file-name-history-use-recentf t
helm-ff-auto-update-initial-value t
helm-M-x-always-save-history t)
;; adaptive
(setq helm-adaptive-history-file (expand-file-name "helm-adaptive-history" fn/cache-dir)
helm-adaptive-history-length 100)
(helm-adaptive-mode t))
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")
(fn/use-feature helm-projectile
(helm)
(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"
(lexical-let*
((fn-notation
(lambda (path)
(lexical-let ((fn-pieces (f-split path)))
(string-join (reverse fn-pieces) fmc/reverse-notation-separator))))
(relative-parent-path
(lambda (path relative-path)
(lexical-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)
(lexical-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)
(lexical-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
(lexical-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))
smex
is faster than helm-M-x
(defconst fn/smex-module-dir (expand-file-name "smex/" fn/custom-module-dir)
"Archived `smex' packages.")
(defconst fn/helm-smex-module-dir (expand-file-name "helm-smex/" fn/custom-module-dir)
"Archived `helm-smex' packages.")
(unless noninteractive
(use-package smex
:load-path fn/smex-module-dir
:demand t
:config
(setq smex-save-file (expand-file-name "smex-items" fn/cache-dir)
smex-history-length 100)))
(use-package helm-smex
:load-path fn/helm-smex-module-dir
:after helm
:bind (("M-x" . helm-smex))
:config
(setq helm-smex-show-bindings t
smex-history-length (or helm-adaptive-history-length smex-history-length)))
Flex matching is strong
(use-package helm-flx
:ensure t
:after helm
:config
(helm-flx-mode t))
A lighter and faster library than helm that uses the minibuffer instead of a separate window.
(use-package ivy
:ensure t
:disabled t
:bind (("M-x" . counsel-M-x))
:config
(setq ivy-use-virtual-buffers t))
Helper functions ere
A command log when needed
(unless noninteractive
(use-package command-log-mode
:ensure t
:diminish command-log-mode
:demand t
:config
(global-command-log-mode t)))
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)))
A mnemonic for key bindings
(unless noninteractive
(use-package which-key
:ensure t
:diminish which-key-mode
:demand t
: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)
(lexical-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)
(lexical-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))))
Likewise with key and functions
(unless noninteractive
(fn/use-feature which-function
nil
(which-function-mode t)))
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))
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))
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))
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))
Anything to manage packages
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))))
Just in case things are down.
(use-package elpa-mirror
:ensure t
:demand t
:config ;; Configuration in personal.el
;; Configure `elpamr-default-output-directory'
(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))))
Visual aesthetics is also a functional thing as well
I like fixed font specially DejaVu Mono
(defconst fn/primary-font-name "DejaVu Sans Mono"
"My ideal font.")
(unless (member fn/primary-font-name (font-family-list))
(message "Hey, you're favorite font isn't installed. You can find a copy in %s and get good." fn/font-dir))
(defconst fn/secondary-font-name "Courier"
"My backup font.")
(defconst fn/primary-font-size 8
"My ideal font size.")
(cond
((member fn/primary-font-name (font-family-list))
(set-frame-font (format "%s-%s" fn/primary-font-name fn/primary-font-size) t t))
((member fn/secondary-font-name (font-family-list))
(set-frame-font (format "%s-%s" fn/secondary-font-name fn/primary-font-size) t t))
(t
(message "Darn it, you have no ideal font set.")))
I prefer no clutter in my screen so I disable majority of the niceties.
(defun fn/optimize-visual-space ()
(let ((try-set-mode (lambda (mode value)
(when (fboundp 'mode)
(mode value)))))
(funcall try-set-mode 'tooltip-mode -1)
(funcall try-set-mode 'tool-bar-mode -1)
(funcall try-set-mode 'menu-bar-mode -1)
(funcall try-set-mode 'fringe-mode 0)))
(fn/optimize-visual-space)
I like dark themes, my eyes respond better to it
My chosen themes
(use-package apropospriate-theme
:ensure t)
(use-package base16-theme
:ensure t)
(defconst fn/tronsesque-package-dir (expand-file-name "tronesque/" fn/custom-module-dir)
"Tronesque package dir")
(use-package tronesque-theme
:load-path fn/tronsesque-package-dir
:demand t)
(use-package material-theme
:ensure t)
Load the theme if it is a terminal or desktop.
(load-theme 'tronesque t)
(tronesque-mode-line)
Somewhat more aesthetic than functional
Just to view hex quicker.
(use-package rainbow-mode
:ensure t
:hook (prog-mode . rainbow-mode)
:commands (rainbow-mode)
:bind (:map fn-standard-prefix-map
("R" . rainbow-mode))
:config
nil)
Got to love that whitespace display
(unless noninteractive
(fn/use-feature global-whitespace
nil
(add-hook 'after-init-hook 'global-whitespace-mode)
(diminish 'global-whitespace-mode)))
Highlight the current line
(use-package hl-line
:diminish hl-line-mode
:init
(global-hl-line-mode t))
When idle time hits
Warm and cozy feeling
(use-package fireplace
:bind (:map fn-standard-prefix-map
("F" . fireplace))
:ensure t
:defer t
:config
(fn/use-feature fireplace-mode--mode-icon
(all-the-icons)
(fn/add-major-mode-icon
'fireplace-mode
(list 'all-the-icons-faicon "fire" :v-adjust -0.0))))
I love to zone out from time to time.
(defconst fn/zone-animation-dir (expand-file-name "custom-zone/" fn/custom-module-dir)
"Custom zone animation directory")
(unless noninteractive
(use-package zone
:ensure t
:commands zone
:defer fn/minute-per-second
:config
(defconst fn/zone-idle-time (* fn/minute-per-second 5)
"Time for zone to zone")
(zone-when-idle fn/zone-idle-time)
(defvar fn/zoning-out-p nil
"Are we zoning yet?")
(defun fn/focused-zone (orig-zone &rest args)
"Only show one window while zoning."
(save-window-excursion
(delete-other-windows)
(setq fn/zoning-out-p t)
(prog1
(apply orig-zone args)
(setq fn/zoning-out-p nil))))
(advice-add #'zone :around #'fn/focused-zone)
;; Garbage collect after zoning
(advice-add #'zone :after #'garbage-collect)
;; Update mode line if there is.
(advice-add #'zone :before #'force-mode-line-update)
(setq zone-programs (list))))
;; Custom zone
(use-package zone-end-of-buffer
:load-path fn/zone-animation-dir
:after zone
:config
(add-to-list 'zone-programs 'zone-end-of-buffer))
(use-package zone-waves
:load-path fn/zone-animation-dir
:after zone
:config
(add-to-list 'zone-programs 'zone-waves))
;; Packaged zones
(defconst fn/zone-matrix-dir (expand-file-name "zone-matrix" fn/custom-module-dir)
"My zone matrix dir.")
(use-package zone-matrix
:load-path fn/zone-matrix-dir
:after zone
:config
(add-to-list 'zone-programs 'zone-matrix)
(setq zmx-update-time (/ 30.0)
zmx-update-speed-factor (/ 60.0)))
(use-package zone-nyan
:ensure t
:after zone
:config
(add-to-list 'zone-programs 'zone-nyan))
(use-package zone-sl
:ensure t
:after zone
:config
(add-to-list 'zone-programs 'zone-sl))
Whatever pertains to coding
Making scratch behave better.
(unless noninteractive
(use-package scratch
:ensure t
:demand t))
(use-package scratch-ext
:ensure t
:after scratch
:config
(setq scratch-ext-log-directory (expand-file-name ".scratch" fn/cache-dir)))
Seems like a good mode
(use-package focus
:ensure t
:defer t
:bind (:map fn-standard-prefix-map
("f" . focus-mode))
:config
nil)
Syntax highlighting is a requirement
(global-font-lock-mode t)
(setq font-lock-support-mode 'jit-lock-mode)
(setq font-lock-maximum-decoration t)
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"))
Make things easier to see
(use-package color-identifiers-mode
:ensure t
:defer t
:diminish color-identifiers-mode
)
Also a vital thing to keeping things highlighted
(show-paren-mode t)
(setq show-paren-style 'expression)
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)))))
The mode line customization.
(use-package moder
:load-path fn/custom-script-dir
:unless noninteractive
:demand t
:config
nil)
Making Emacs more of an organization tool.
Since Emacs doesn’t have dialog boxes, it is sufficient to have fringe or message alerts.
(use-package alert
:ensure 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)
(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."
(lexical-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)))
(lexical-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))
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)))
This makes more than a editor
Primarily I use org-drill
and org-journal
(use-package org
:ensure t
: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
:after org
:config
nil)
Configuration for org-src
(fn/use-feature org-src
(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))
(fn/use-feature org-src-alert
(org alert)
nil)
(fn/use-feature org-todo
(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)))))
(fn/use-feature org-src-desktop
(org desktop)
(add-to-list 'desktop-modes-not-to-save 'org-src-mode))
(fn/use-feature org-src-edit
(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))
More configurations for the capture
(fn/use-feature org-capture
(org)
nil ;; Configuration in personal.el
)
More configuration for org-agenda
(fn/use-feature org-agenda
(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))
Some refiling actions perhaps?
(fn/use-feature org-archive
(org)
(setq org-log-refile 'time
org-refile-targets nil))
Archiving is needed to avoid bloating.
(fn/use-feature org-refile
(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))
;; Archive done tasks on load
(add-hook
'after-init-hook
(apply-partially 'run-with-idle-timer 0 nil 'fn/org-archive-done-agenda-tasks)))
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))
(fn/use-feature org-journal-mode--mode-icon
(all-the-icons)
(fn/add-major-mode-icon
'org-journal-mode
(list 'all-the-icons-faicon "pencil-square-o" :v-adjust 0.0)))))
A nice presentation framework
(unless noninteractive
(defconst fn/org-reveal-module-dir (expand-file-name "~/Modules/reveal.js")
"reveal.js directory")
(fn/use-feature ox-reveal
(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)))))
Configuration for org-clock
(fn/use-feature org-clock
(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))
Flash cards are a must
(unless noninteractive
(fn/use-feature org-drill
(org)
(use-package org-drill
:after org
:bind (:map fn-standard-prefix-map
("D" . org-drill))
:demand t
:config
(add-to-list 'org-modules 'org-drill)
;; More at personal.el
)))
Mobile org document
(unless noninteractive
(when (boundp 'fn/org-dir)
(fn/use-feature org-mobile
(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))))
Using helm
to navigate org-mode
.
(fn/use-feature org-helm-completing-read
(helm org)
;; 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)))
Assist library when working with code blocks
(with-eval-after-load 'org
(require 'org-tempo)
;;; Bug on first element ("n" "#+BEGIN_NOTES ? #+END_NOTES")
(setq org-structure-template-alist (cdr org-structure-template-alist)))
Having a good spell checker is a must. I use flyspell
although I have read issues about it
(fn/use-executables flyspell-check
(aspell)
(use-package flyspell
:ensure t
:if (not (or (eq system-type 'windows-nt) noninteractive))
: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"))))
Also a good grammar checker.
(fn/use-executables java-langtool
(java languagetool)
(use-package langtool
:ensure t
:unless 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 ;; Taken from `languagetool' script to execute command
langtool-language-tool-jar
nil
langtool-java-classpath
(string-join
(cons
"/usr/share/languagetool"
(directory-files "/usr/share/java/languagetool" t "\\.*jar"))
":")
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)))
Directory management for Emacs
(require 'dired-x) ;; Allows multi open marked files
(setq dired-dwim-target t
dired-listing-switches "-alh"
dired-recursive-copies 'always ;; Don't ask because I did it
dired-recursive-deletes 'always
dired-isearch-filenames t
dired-copy-preserve-time t)
(dired-async-mode t)
(add-hook 'dired-mode-hook 'dired-hide-details-mode)
(fn/use-feature wdired-mode--mode-icon
(all-the-icons)
(fn/add-major-mode-icon
'wdired-mode
'(all-the-icons-octicon "diff" :v-adjust 0.0)))
Text is not enough, images are important too
(use-package image-dired
:ensure t
:unless noninteractive
:after dired
:config
(setq image-dired-dir (expand-file-name "image-dired" fn/cache-dir-name)
image-dired-gallery-dir image-dired-dir
image-dired-db-file (expand-file-name ".image-dired_db" image-dired-dir)
image-dired-temp-image-file (expand-file-name ".image-dired_temp" image-dired-dir)
image-dired-temp-image-file (expand-file-name ".image-dired_rotate_temp" image-dired-dir)
image-dired-main-image-directory "~/Pictures"))
Tramp is too good to pass up
(use-package tramp
:after eshell
:config
(setq tramp-persistency-file-name (expand-file-name "tramp" fn/cache-dir)
tramp-default-user (getenv "USER")
tramp-verbose 6)
;; Make SSH work faster by reusing connections
(setq tramp-ssh-controlmaster-options
"-o ControlMaster=auto -o ControlPath='tramp.%%C' -o ControlPersist=no"))
Something to keep track of my finances
(fn/use-executables ledger-check
(ledger)
(use-package ledger-mode
:ensure t
:unless 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"))
(fn/use-feature ledger-mode--mode-icon
(all-the-icons)
(fn/add-major-mode-icon
'ledger-mode
'(all-the-icons-faicon "money" :v-adjust -0.1)))
(fn/use-feature ledger-report-mode--mode-icon
(all-the-icons)
(fn/add-major-mode-icon
'ledger-report-mode
'(all-the-icons-faicon "line-chart" :v-adjust -0.1))))
(use-package flycheck-ledger
:ensure t
:hook (ledger-mode . flycheck-mode)
:config
(add-to-list 'flycheck-enabled-checkers 'ledger)))
Man pages are nice
(fn/use-executables man-check
(man)
(use-package woman
:ensure t
:bind (:map fn-standard-prefix-map
("C-w" . woman))
:config
nil))
Reading mail in Emacs
(with-eval-after-load 'prodigy
(prodigy-define-tag
:name 'imap)
(when (executable-find "mbsync")
(defconst fn/imap-mbsync-service-name "imap-mbsync"
"The IMAP `mbsync' service name.")
(fn/prodigy-define-service
:name fn/imap-mbsync-service-name
:tags '(imap mbsync)
;; Custom binding
:bind-command-name "mbsync"
:bind-map fn/prodigy-map
:bind (kbd "g m"))
(progn
(require 'alert)
(defun fn/prodigy-mbsync-state-alert (service state)
"`mbsync' notify alert."
;; Color Palette: http://www.color-hex.com/color-palette/201
(lexical-let ((service-name (plist-get service :name)))
(when (string= service-name fn/imap-mbsync-service-name)
(pcase state
('ready (fn/alert-color
(format "[mbsync] %s is ready" service-name)
:color "#0000ff"))
('error (fn/alert-color
(format "[mbsync] %s is ready" service-name)
:color "#bc4b4b"))
('synced (fn/alert-color
(format "[mbsync] %s synced an account" service-name)
:color "#eeeeee"))))))
(add-hook
'fn/prodigy-mbsync-state-change-hook
#'fn/prodigy-mbsync-state-alert))
(setq fn/imap-mbsync-service (prodigy-find-service fn/imap-mbsync-service-name)))
(when (executable-find "offlineimap")
(defconst fn/imap-offlineimap-service-name "imap-offlineimap"
"The IMAP `offlineimap' service name.")
(fn/prodigy-define-service
:name fn/imap-offlineimap-service-name
:tags '(imap offlineimap)
;; Custom binding
:bind-command-name "imap"
:bind-map fn/prodigy-map
:bind (kbd "g g"))
(progn
(require 'alert)
(defun fn/prodigy-offlineimap-state-alert (service state)
"`offlineimap' notify alert."
;; Color Palette: http://www.color-hex.com/color-palette/201
(lexical-let ((service-name (plist-get service :name)))
(when (string= service-name fn/imap-offlineimap-service-name)
(pcase state
('ready (fn/alert-color
(format "[offlineimap] %s is ready" service-name)
:color "#0000ff"))
('error (fn/alert-color
(format "[offlineimap] %s has a fetch error. Check out the logs" service-name)
:color "#bc4b4b"))
('synced (fn/alert-color
(format "[offlineimap] %s synced an account" service-name)
:color "#eeeeee"))))))
(add-hook
'fn/prodigy-offlineimap-state-change-hook
#'fn/prodigy-offlineimap-state-alert))
(setq fn/imap-offlineimap-service (prodigy-find-service fn/imap-offlineimap-service-name))))
The ultimate mail browser, majority of the configuration is in ~/.gnus
(unless noninteractive
(when (file-exists-p (expand-file-name ".gnus" "~"))
(use-package gnus
:bind (:map fn-standard-prefix-map
("g g" . gnus)
("g G" . gnus-other-frame))
:defer t
:config
nil ;; Config at `~/.gnus'
)))
A quick alternative for gnus
(unless noninteractive
(fn/use-executables mu4e-check
(mu)
(defconst fn/mu-package-dir
"/usr/share/emacs/site-lisp/mu4e/"
"Installed via Arch Linux mu package")
(use-package mu4e
:load-path "/usr/share/emacs/site-lisp/mu4e/"
:if (file-exists-p "/usr/share/emacs/site-lisp/mu4e/")
:bind (:map fn-standard-prefix-map
("g m" . mu4e))
:config
(setq mu4e-maildir (expand-file-name "Maildir" "~")
mu4e-get-mail-command "true")
(shell-command (format "mu index --maildir=%s" mu4e-maildir))
(mapc
(lambda (internal-maildir)
(let ((maildir (expand-file-name internal-maildir mu4e-maildir)))
(unless (file-exists-p maildir)
(make-directory maildir))))
'("sent" "drafts" "trash"))
(setq mu4e-sent-messages-behavior 'delete
mu4e-update-interval 60
mu4e-index-cleanup nil
mu4e-index-lazy-check t
mu4e-headers-results-limit 1000)
(setq mu4e-completing-read-function 'completing-read
mu4e-context-policy 'pick-first
mu4e-compose-context-policy nil
mu4e-confirm-quit nil)
(setq mu4e-headers-date-format "%F %H:%M"
mu4e-view-show-addresses nil
mu4e-headers-fields '((:date . 25)
(:flags . 6)
(:from . 22)
(:subject . nil)))
(setq mu4e-attachment-dir (expand-file-name "Downloads" "~"))
(setq mu4e-hide-index-messages t)
(setq message-citation-line-format "%N @ %Y-%m-%d %H:%M %Z:\n"
message-citation-line-function 'message-insert-formatted-citation-line)
(fn/use-feature restclient-mode--mode-icon
(mu4e all-the-icons)
(fn/add-major-mode-icon
'mu4e-headers-mode
(list 'all-the-icons-faicon "newspaper-o" :v-adjust -0.1))
(fn/add-major-mode-icon
'mu4e-main-mode
(list 'all-the-icons-faicon "envelope" :v-adjust -0.1))
(fn/add-major-mode-icon
'mu4e-compose-mode
(list 'all-the-icons-faicon "paper-plane" :v-adjust -0.1))
(fn/add-major-mode-icon
'mu4e-view-mode
(list 'all-the-icons-faicon "envelope-o" :v-adjust -0.1)))
(fn/use-feature mu4e-flyspell
(mu4e flyspell)
(add-hook 'mu4e-compose-mode-hook #'flyspell-mode))
(fn/use-feature org-mu4e
(org mu4e)
(setq org-mu4e-link-query-in-headers-mode nil))
(fn/use-feature mu4e-alert
(mu4e alert)
(use-package mu4e-alert
:ensure t
:defer t
:after (mu4e alert)
:functions (mu4e-alert-enable-notifications)
:init
(mu4e-alert-enable-notifications)
:config
(setq mu4e-alert-email-notification-types `(count)
mu4e-alert-style 'libnotify)
(progn ;; Mail count, along-side mu4e-alert
(defvar fn/mu4e-current-unread-message-count 0
"The current `mu4e' unread message count.")
(defun fn/mu4e-alert--get-mu-unread-mails (orig-fun callback)
"Just `mu4e-alert--get-mu-unread-mails' but the CALLBACK updating the message count."
(lexical-let ((callback callback))
(funcall orig-fun
(lambda (mails)
(prog1
(funcall callback mails)
(setq fn/mu4e-current-unread-message-count (length mails)))))))
(advice-add 'mu4e-alert--get-mu-unread-mails :around #'fn/mu4e-alert--get-mu-unread-mails))))
(fn/use-executables w3m-check
(w3m) ;; Use `w3m' to render the pages.
(setq mu4e-html2text-command "w3m -dump -T text/html"
mu4e-view-show-images t)))))
Alternative to gnus
(use-package wanderlust
:ensure t
:defer t)
Sending mail is just as important
(unless noninteractive
(use-package smtpmail ;; Requires gnutls for ArchLinux
:defer t
:functions (smtpmail-send-it)
:init
(setq message-send-mail-function 'smtpmail-send-it)
:config
(setq smtpmail-debug-info t)
(defun fn/smtpmail-gmail-server ()
"Configure `smtpmail' for `gmail' SMTP server."
(interactive)
(setq smtpmail-stream-type 'starttls
smtpmail-smtp-server "smtp.gmail.com"
smtpmail-smtp-service 587))
(defun fn/smtpmail-yandex-server ()
"Configure `smtpmail' for `gmail' SMTP server."
(interactive)
(setq smtpmail-stream-type 'ssl
smtpmail-smtp-server "smtp.yandex.com"
smtpmail-smtp-service 465))
;; mail.yandex.com is a bit more loose
;; (fn/smtpmail-yandex-server)
))
Sometimes when you just need proof
(use-package emacsshot
:ensure t
:bind (:map fn-standard-prefix-map
("C-c w" . emacsshot-snap-window)
("C-c f" . emacsshot-snap-frame))
:config
(defconst fn/emacsshot-dir (expand-file-name "emacsshot" fn/extra-dir)
"My screenshot directory")
(unless (f-exists-p fn/emacsshot-dir)
(make-directory fn/emacsshot-dir))
(setq emacsshot-with-timestamp t
emacsshot-snap-frame-filename (expand-file-name "my-frame.png" fn/emacsshot-dir)
emacsshot-snap-window-filename (expand-file-name "my-window.png" fn/emacsshot-dir)))
Making demonstrations in Emacs is a must. My thanks to this blog post.
(fn/use-executables camcorder-check
(recordmydesktop)
(use-package camcorder
:ensure t
:unless noninteractive
:bind (("<f10>" . camcorder-record))
:config
(defconst fn/camcorder-dir (expand-file-name "camcorder" fn/extra-dir)
"My camcorder directory.")
(make-directory fn/camcorder-dir t)
(setq camcorder-output-directory fn/camcorder-dir
camcorder-gif-output-directory fn/camcorder-dir
camcorder-frame-parameters
(list (cons 'name "Screencast")
(cons 'height (max (/ (frame-height) 2) 40 ))
(cons 'width (max (/ (frame-width) 2) 80 ))
(cons 'top 20)
(cons 'left 20)))
(defvar fn/camcorder-state nil
"The state of the recording. Possible values are `nil', `recording' and `paused'.")
(defvar fn/camcorder-start-time nil
"The start time of a recording.")
(defun fn/camcorder-state-start ()
"Set the start state of the recording."
(setq fn/camcorder-state 'recording
fn/camcorder-start-time (current-time))
(select-frame-set-input-focus camcorder-recording-frame))
(defun fn/camcorder-state-pause ()
"Set the pause state of the recording."
(cond
((null fn/camcorder-state)
nil)
((eq fn/camcorder-state 'recording)
(setq fn/camcorder-state 'paused))
((eq fn/camcorder-state 'paused)
(setq fn/camcorder-state 'recording))))
(defun fn/camcorder-state-stop ()
"Set the stop state of the recording."
(setq fn/camcorder-state nil
fn/camcorder-start-time nil))
(advice-add 'camcorder-record :after #'fn/camcorder-state-start)
(advice-add 'camcorder-pause :before #'fn/camcorder-state-pause)
(advice-add 'camcorder-stop :before #'fn/camcorder-state-stop)
;; Preload
(defconst fn/camcorder-demo-file (expand-file-name "camcorder-demo.txt" fn/custom-script-dir)
"Initial buffer when recording.")
(defconst fn/camcorder-demo-buffer-name "*Space Channel 5"
"The name of the demo buffer.")
(defun fn/camcorder-demo-buffer ()
"Open a blank special buffer when recording."
(let ((demo-buffer (find-file fn/camcorder-demo-file)))
(with-current-buffer demo-buffer
(switch-to-buffer demo-buffer)
(rename-buffer fn/camcorder-demo-buffer-name)
(emacs-lisp-mode))))
(defconst fn/camcorder-init-file (expand-file-name "camcorder-init.el" fn/custom-script-dir)
"Init script when recording.")
(defun fn/camcorder-load-init ()
"Load camcorder init for test."
(load fn/camcorder-init-file t))
(defun fn/camcorder-init ()
"Load an initial script when recording with a little delay."
(run-with-idle-timer 1 nil #'fn/camcorder-load-init))
(defun fn/camcorder-ready-message ()
"Message when camcorder starts."
(message "Hey there, space cats! Ulala here, comin' at you from Spaceport 9."))
(add-hook 'camcorder-mode-hook #'fn/camcorder-demo-buffer t)
(add-hook 'camcorder-mode-hook #'fn/camcorder-load-init t)
(add-hook 'camcorder-mode-hook #'fn/camcorder-ready-message t)))
A better way of viewing processes
(defconst fn/list-processes+-module-dir (expand-file-name "list-processes+/" fn/custom-module-dir)
"Archived `list-processes+' packages.")
(use-package list-processes+
:load-path fn/list-processes+-module-dir
:bind (:map fn-standard-prefix-map
("M-p" . list-processes+))
:config
nil)
Modes for my programming experiences
Generic support for coding
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)
Enable some literate programming
(fn/use-feature org-literate
(org)
(require 'ob-shell))
This is as handy as paredit
(use-package smartparens
:diminish smartparens-mode
:ensure t
:init
(add-hook 'prog-mode-hook 'smartparens-mode)
:config
(require 'smartparens-config)
(define-key smartparens-mode-map (kbd "C-<left>") 'sp-forward-slurp-sexp)
(define-key smartparens-mode-map (kbd "C-<up>") 'sp-splice-sexp))
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))
Enough said, magit is the best git client you can get anywhere.
(fn/use-executables git-check
(git)
(use-package magit
:ensure t
: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
(rx bos "COMMIT_EDITMSG")
(cons 'display-buffer-same-window (list))))
(with-eval-after-load 'projectile
(make-variable-buffer-local 'magit-git-environment)
(add-hook 'magit-process-mode-hook #'fn/safe-load-project-local-file)
(add-hook 'magit-status-mode-hook #'fn/safe-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
:defer t
:unless noninteractive
:bind (("M-g t" . git-timemachine))
:config
nil)
A tool to help with github.
;; Dubious extension for the meantime
(use-package magithub
:ensure t
:disabled t
:defer t
:after (magit)
:config
(magithub-feature-autoinject t))
Support for SVN with the awesome magit
interface
(use-package magit-svn
:ensure t
:after magit)
Code folding is helpful
(use-package origami
:ensure t
:defer t
:config
(add-to-list 'origami-parser-alist '(json-mode . origami-javascript-style-parser)))
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)
(fn/use-feature gist-list--mode-icon
(all-the-icons)
(fn/add-major-mode-icon
'gist-list-mode
(list 'all-the-icons-octicon "checklist" :v-adjust 0.0))))
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))))
The silver searcher is a fine tool
(unless noninteractive
(fn/use-executables ag-check
(ag)
(use-package ag
:ensure t
:defer t
:commands (ag)
:config
(setq ag-highlight-search t
ag-reuse-window t
ag-reuse-buffers t)
(add-to-list
'display-buffer-alist
(cons
(rx bos "*ag " (zero-or-more anything) "*" eos)
(cons 'display-buffer-same-window (list))))
(fn/use-feature restclient-mode--mode-icon
(ag all-the-icons)
(fn/add-major-mode-icon
'ag-mode
(list 'all-the-icons-faicon "binoculars" :v-adjust -0.1))))))
With helm
(fn/use-feature helm-ag
(helm ag)
(use-package helm-ag
:ensure t
:demand t
:config
nil))
Allow for ripgrep
(unless noninteractive
(fn/use-executables ripgrep-check
(rg)
(use-package ripgrep
:ensure t
:defer t
:after projectile
:config
(add-to-list
'display-buffer-alist
(cons
(rx bos "*ripgrep-search*" eos)
(cons 'display-buffer-same-window (list))))
(fn/use-feature ripgrep-mode--mode-icon
(ripgrep all-the-icons)
(fn/add-major-mode-icon
'ripgrep-search-mode
(list 'all-the-icons-faicon "binoculars" :v-adjust -0.1))))))
Dump jumping with ag
(use-package dumb-jump
:ensure t
:bind (:map fn-standard-prefix-map
("C-d g" . dumb-jump-go)
("C-d b" . dumb-jump-back)))
Jump and go is really a must needed feature
(fn/use-executables global-check
(ggtags global)
(use-package ggtags
:ensure t
:defer t
:functions 'ggtags-eldoc-function
:init
(defun fn/ggtags-c-like-mode ()
"Turn on GGTAGS if it is a c like mode"
(when (derived-mode-p 'c-mode 'c++-mode 'java-mode 'javascript-mode)
(ggtags-mode t)))
(add-hook 'prog-mode-hook #'fn/ggtags-c-like-mode)
:config
(fn/use-feature eldoc-ggtags
(eldoc)
(setq-local eldoc-documentation-function #'ggtags-eldoc-function))
(use-package helm-gtags
:ensure t
:after ggtags
:config
nil)))
Something to speed up searching
(require 'dash)
(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 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)))
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)
(fn/use-feature restclient-mode--mode-icon
(restclient all-the-icons)
(fn/add-major-mode-icon
'restclient-mode
(list 'all-the-icons-faicon "arrow-circle-o-up" :v-adjust -0.1)))
(with-eval-after-load 'smartparens
(add-hook 'restclient-mode-hook 'smartparens-mode))
(with-eval-after-load 'paredit
(add-hook 'restclient-mode-hook 'paredit-mode))
(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)))
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)))
YAML editing
(use-package yaml-mode
:ensure t
:defer t)
TOML editing
(use-package toml-mode
:ensure t
:defer t)
JSON support
(use-package json-mode
:ensure t
:defer t
:mode ("\\.json\\'" . json-mode)
:config
(fn/use-executables jsonlint-check
(jsonlint)
(fn/use-feature flycheck-json
(json-mode flycheck)
(add-to-list 'flycheck-enabled-checkers 'json-jsonlint))))
(use-package json-snatcher
:ensure t
:defer t
:after (json-mode))
(use-package json-reformat ;; Native tool for json formatting
:ensure t
:defer t
:after (json-mode))
(fn/use-executables js-beautify-check
(js-beautify) ;; sudo npm -g install js-beautify
(use-package web-beautify ;; A bit more nodejs
:ensure t
:defer t
:after json-mode))
GraphQL support
(use-package graphql-mode
:ensure t
:defer t)
Markdown editing
(use-package markdown-mode
:ensure t
:defer t
:config nil)
Expression building.
(use-package evalator
:ensure t
:defer t
:bind (:map fn-standard-prefix-map
("C-e e" . evalator)
("C-e x" . evalator-explicit)
("C-e r" . evalator-resume)
("C-e i" . evalator-insert-equiv-expr))
:config
nil)
Quickly evaluate regions
(unless noninteractive
(use-package quickrun
:ensure t
:defer t
:config
nil))
Not merely a tree but a structure.
speedbar
is a built-in default.
(unless noninteractive
(use-package speedbar
:defer t
:config
;; I prefer the same frame version
(fn/use-feature speedbar-golden-ratio
(speedbar golden-ratio)
(add-to-list 'golden-ratio-exclude-modes 'speedbar-mode))))
(defconst fn/sr-speedbar-extras-dir (expand-file-name "sr-speedbar-extras" fn/custom-module-dir)
"Some extra `sr-speedbar' scripts.")
(use-package sr-speedbar
:ensure t
:defer t
:bind (:map fn-standard-prefix-map
("t o" . sr-speedbar-open)
("t t" . sr-speedbar-toggle))
:after (speedbar)
:commands (sr-speedbar-open)
:config
(setq sr-speedbar-auto-refresh t
sr-speedbar-right-side t
sr-speedbar-default-width 15
sr-speedbar-width 15
sr-speedbar-max-width 25)
(fn/use-feature workgroups-speedbar
(workgroups2 sr-speedbar)
(add-hook 'wg-before-switch-to-workgroup-hook #'sr-speedbar-close))
(fn/use-feature projectile-speedbar
(projectile sr-speedbar)
(use-package projectile-speedbar
:ensure t
:defer t
:after (projectile sr-speedbar)
:config
(remove-hook 'projectile-find-dir-hook 'projectile-speedbar-open-current-buffer-in-tree)
(remove-hook 'projectile-find-file-hook 'projectile-speedbar-open-current-buffer-in-tree)))
(use-package sb-imenu
:load-path fn/sr-speedbar-extras-dir
:after (sr-speedbar)
:config
(setq speedbar-initial-expansion-list-name "sb-imenu")))
I hope the rx
syntax can be ported to other languages.
(unless noninteractive
(use-package pcre2el
:ensure t
:defer t
:commands (rxt-global-mode)
:config
nil))
Using Redmine
in any means necessary.
(use-package org-redmine
:ensure t
:defer t
:config
;; Use redmine auth api key
nil)
(defconst fn/redmine-package-dir (expand-file-name "emacs-redmine" fn/custom-module-dir)
"My local `redmine' package dir.")
(use-package redmine
:load-path fn/redmine-package-dir
:defer t
:config
(setq redmine-program (expand-file-name "redmine.py" fn/redmine-package-dir)))
Need Jira support
(unless noninteractive
(defconst org-jira-keymap (make-sparse-keymap)
"Shiv keymap for org-jira")
(use-package org-jira
:ensure t
:defer t
:bind-keymap ("C-c j" . org-jira-keymap)
:config
;; Project specific configuration
(setq jiralib-url nil)
(define-key org-jira-keymap (kbd "p p") 'org-jira-get-projects)
(define-key org-jira-keymap (kbd "i b") 'org-jira-browse-issue)
(define-key org-jira-keymap (kbd "i i") 'org-jira-get-issues)
(define-key org-jira-keymap (kbd "i h") 'org-jira-get-issues-headonly)
(define-key org-jira-keymap (kbd "i u") 'org-jira-update-issue)
(define-key org-jira-keymap (kbd "i p") 'org-jira-progress-issue)
(define-key org-jira-keymap (kbd "i P") 'org-jira-progress-issue-next)
(define-key org-jira-keymap (kbd "i a") 'org-jira-assign-issue)
(define-key org-jira-keymap (kbd "i r") 'org-jira-refresh-issue)
(define-key org-jira-keymap (kbd "i R") 'org-jira-refresh-issues-in-buffer)
(define-key org-jira-keymap (kbd "i c") 'org-jira-create-issue)
(define-key org-jira-keymap (kbd "i k") 'org-jira-copy-current-issue-key)
(define-key org-jira-keymap (kbd "s c") 'org-jira-create-subtask)
(define-key org-jira-keymap (kbd "s s") 'org-jira-get-subtasks)
(define-key org-jira-keymap (kbd "c u") 'org-jira-update-comment)
(define-key org-jira-keymap (kbd "c C") 'org-jira-update-worklogs-from-org-clocks)
(define-key org-jira-keymap (kbd "t t") 'org-jira-todo-to-jira)
(define-key org-jira-keymap (kbd "f f") 'org-jira-get-issues-by-fixversion)))
Google Translate support for those RTL times
(unless noninteractive
(use-package google-translate
:ensure t
:defer t
:commands (google-translate-at-point google-translate-query-translate fn/google-translate-at-point)
:config
(require 'google-translate-default-ui)
(setq google-translate-default-source-language "en"
google-translate-default-target-language "ar")
(setq google-translate-output-destination 'current-buffer)
(defun fn/google-translate-at-point ()
"Translate at point while replacing the text."
(interactive)
(when (region-active-p)
(let ((beginning (region-beginning))
(end (region-end))
(google-translate-output-destination 'current-buffer))
(google-translate-at-point)
(kill-region beginning end))))))
CSV moder
(unless noninteractive
(use-package csv-mode
:ensure t
:defer t
:config
nil))
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))
Support editor config for some project.
(use-package editorconfig
:ensure t
:hook (prog-mode . editorconfig-mode)
:config
nil)
A little thesaurus help.
(use-package powerthesaurus
:ensure t
:commands (powerthesaurus-lookup-word))
Support for making diagrams
(defconst fn/plantuml-file (expand-file-name "plantuml.jar" "~"))
;;; Download link: http://sourceforge.net/projects/plantuml/files/plantuml.jar/download
(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-babel-load-languages '(plantuml . t))
(setq org-plantuml-jar-path fn/plantuml-file)))
Specific helper modes
Call npm with Emacs
(fn/use-executables npm-check
(node npm)
(use-package npm-mode
:ensure t
:defer t
:bind (:map fn-standard-prefix-map
("n" . npm-mode))
:init
(setq npm-mode-command-prefix "C-c n n")))
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))
Makes you a good lisp developer and quite addictive to have
(use-package paredit
:ensure t
:defer t
:diminish paredit-mode
:init
(add-hook 'emacs-lisp-mode-hook #'enable-paredit-mode)
(add-hook 'ielm-mode-hook #'enable-paredit-mode)
(add-hook 'eval-expression-minibuffer-setup-hook #'enable-paredit-mode)
(add-hook 'lisp-mode-hook #'enable-paredit-mode)
(add-hook 'lisp-interaction-mode-hook #'enable-paredit-mode))
(fn/use-feature paredit-eldoc
(eldoc)
(eldoc-add-command 'paredit-backward-delete
'paredit-close-round))
Emacs Lisp refactor mode
(use-package emr
:ensure t
:defer t
:init
(define-key prog-mode-map (kbd "M-RET") 'emr-show-refactor-menu)
(add-hook 'prog-mode-hook 'emr-initialize))
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))
Some testing libraries
(use-package buttercup
:ensure t
:defer t)
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)
(fn/use-feature cask-mode--mode-icon
(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)
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))
If enterprise grade support is needed.
(fn/use-executables lein-check
(lein)
(use-package ejc-sql
:ensure t
:defer t
:after (sql)
:functions (ejc-create-connection)
:commands (ejc-connect)
:config
(setq ejc-set-rows-limit 1000
nrepl-sync-request-timeout 60)))
Sometimes it’s just easier to get a scratch pad.
(fn/use-feature org-literate-sql
(org sql)
(require 'ob-sql) ;; Default
(use-package ob-sql-mode
:ensure t
:defer t
:after (org sql)
:config
nil ;; None yet
))
Some utility when editing SQL files
(unless noninteractive
(fn/use-executables ruby-sql-formatter
(anbt-sql-formatter) ;; gem install anbt-sql-formatter
(defun fn/sql-beautify-region (beg end)
"Beautify SQL in region between beg and END."
(interactive "r")
(save-excursion
(shell-command-on-region beg end "anbt-sql-formatter" nil t)))
(defun fn/sql-beautify-buffer ()
"Beautify SQL in buffer."
(interactive)
(fn/sql-beautify-region (point-min) (point-max))))
(fn/use-executables python-sql-formatter
(format-sql) ;; pip install --user format-sql
(use-package format-sql
:ensure t
:defer t
:commands (format-sql-buffer format-sql-region))))
I need me some SQL support for NoSQL types
(use-package ob-mongo
:ensure t
:defer t
:after org)
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)
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)
(fn/use-feature js2-jsx-mode--mode-icon
(all-the-icons)
(fn/add-major-mode-icon
'js2-jsx-mode
(list 'all-the-icons-alltheicon "javascript" :v-adjust 0.1))))
My linter if you will for JS.
(unless noninteractive
(fn/use-feature flycheck-eslint-relative
(flycheck 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 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)
)
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 support
(use-package json-snatcher
:ensure t
:after js2-mode
:bind (("C-c C-g" . jsons-print-path)))
Let’s support literate programming for JS
(fn/use-feature org-js2-src
(org js2-mode)
(add-to-list 'org-babel-load-languages '(js . t)))
My favorite programming language, unassuming like Marcy from Peanuts
(fn/use-executables python-check
(python pip)
(use-package python
:ensure t
:defer t
:config
nil))
Pep me bro
(use-package py-autopep8
:ensure t
:defer t
:after python)
Using Ipython
for this
(use-package ein
:ensure t
:defer t
:config
(setq ein:use-auto-complete t
ein:use-smartrep t))
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))
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))
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)
Let’s add a link to org-babel
(fn/use-feature org-python-src
(org python)
(add-to-list 'org-babel-load-languages '(python .t)))
The defacto for Haskell development
(fn/use-executables haskell-check
(ghci)
(use-package haskell-mode
:ensure t
: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)
(add-hook 'haskell-interactive-mode-hook 'smartparens-mode)
(fn/use-executables haskell-hoogle-check
(hoogle)
(add-hook 'haskell-interactive-mode-hook 'haskell-hoogle-start-server)
(fn/use-feature helm-hoogle
(helm haskell-mode)
(use-package helm-hoogle
:ensure t
:demand t)))
(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)))
(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))))))
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))
Company for Haskell
(unless noninteractive
(fn/use-feature company-haskell
(haskell-mode 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 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)))
Syntax checker for Haskell
(fn/use-feature flycheck-haskell
(flycheck haskell-mode)
(use-package flycheck-haskell
:ensure t
:demand t
:config
(flycheck-haskell-setup))
(use-package flycheck-stack
:ensure t
:after flycheck-haskell
:config
nil))
Easy snippets for Haskell
(unless noninteractive
(fn/use-feature haskell-snippets
(yasnippet haskell-mode)
(use-package haskell-snippets
:ensure t
:demand t)))
Maybe stack or hack?
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
(fn/use-feature org-haskell
(haskell-mode 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)
(lexical-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)
(lexical-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."
(lexical-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)))
;; (add-hook 'org-src-mode-hook #'fn/haskellize-org-haskell-buffer t)
;; (add-hook 'fn/org-haskell-mode-hook 'fn/haskell-process-load-or-reload)
;; (add-hook 'fn/org-haskell-mode-hook 'fn/haskell-reload-on-save)
;; (advice-add 'org-edit-src-save :before #'fn/save-org-haskell-buffer)
;; (advice-add 'org-edit-src-exit :around #'fn/cleanup-org-haskell-buffer)
(fn/use-feature literate-haskell-hindent
(haskell-mode hindent)
;; (add-hook 'fn/org-haskell-mode-hook 'fn/hindent-before-save)
)))
A major mode for purescript.
(use-package purescript-mode
:ensure t
:config
nil)
Flycheck for the win
(use-package flycheck-purescript
:ensure t
:after (flycheck purescript)
:config
(flycheck-purescript-setup))
The default IDE support for purescript.
(use-package psc-ide
:ensure t
:hook (purescript-mode . psc-ide-mode)
:config
(setq psc-ide-use-npm-bin t)
(add-hook 'purescript-mode-hook #'turn-on-purescript-indentation))
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))
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)
:config
(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 flycheck-credo
:ensure t
:after flycheck
:demand t
:config
(flycheck-credo-setup))
(use-package flycheck-mix
:ensure t
:after flycheck
:demand t
:config
(flycheck-mix-setup))
(use-package elixir-yasnippets
:ensure t
:after elixir-mode
:config
nil)
(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)))
A close C alternative that I can chew on
(use-package rust-mode
:ensure t
:config
(setq rust-format-on-save t))
(use-package racer
:ensure t
:after (rust-mode company)
:hook (rust-mode . racer-mode)
:config
(setq racer-cmd (executable-find "racer")))
(use-package cargo
:ensure t
:after rust-mode
:hook (rust-mode . cargo-minor-mode)
:config
(setq cargo-process--command-add "install")
(add-to-list 'cargo-process--no-manifest-commands "Add")
(defun fn/cargo-test-nocapture-flag (orig-fun &rest args)
"For test commands, add `--nocapture' and `--quiet' flags."
(let ((cargo-process--command-flags
(append cargo-process--command-flags
"-- --nocapture --quiet")))
(apply orig-fun args)))
(advice-add 'cargo-process--command-test :around #'fn/cargo-test-nocapture-flag)
(advice-add 'cargo-process-test :around #'fn/cargo-test-nocapture-flag)
(advice-add 'cargo-process-current-test :around #'fn/cargo-test-nocapture-flag)
(add-to-list
'display-buffer-alist
(cons
(rx bos "*Cargo" (* anything))
(cons 'display-buffer-same-window (list)))))
(use-package flycheck-rust
:ensure t
:after flycheck
:demand t
:config
(flycheck-rust-setup))
The major mode for PHP
(use-package php-mode
:ensure t
:defer t
:config
(setq php-mode-coding-style 'psr2
php-style-delete-trailing-whitespace t)
(add-hook 'php-mode-hook 'php-enable-default-coding-style))
The usual completion
(use-package company-php
:ensure t
:defer t
:disabled t
:after company
:init
(add-hook 'php-mode-hook 'company-mode)
:config
(add-to-list 'company-backends 'company-ac-php-backend))
Formatter via phpcbf
(unless noninteractive
(fn/use-executables php-formatter-check
(phpcbf)
(use-package phpcbf
:ensure t
:defer t
:after (php-mode)
:config
(setq phpcbf-standard "PSR2"
phpcbf-executable "phpcbf")
(add-hook 'php-mode-hook 'phpcbf-enable-on-save))))
Flycheck to the rescue
(fn/use-feature flycheck-php
(flycheck php-mode)
(fn/use-executables php-linter-check
(phpcs)
(setq flycheck-phpcs-standard "PSR2"
flycheck-php-phpcs-executable "phpcs")
(add-to-list 'flycheck-enabled-checkers 'php)
(add-to-list 'flycheck-enabled-checkers 'php-phpcs)))
Built in ruby-mode
is enough
(unless noninteractive
(fn/use-feature company-robe
(robe company)
(add-to-list 'company-backends 'company-robe)))
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))
Some editing tools
(unless noninteractive
(use-package ruby-tools
:ensure t
:init
(add-hook 'ruby-mode-hook #'ruby-tools-mode)
:config
nil))
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)
Block chain development.
(use-package solidity-mode
:ensure t
:config
nil)
(use-package solidity-flycheck
:ensure t
:hook solidity
:config
nil)
(use-package company-solidity
:ensure t
:hook solidity
:config
nil)
For C# development
(use-package omnisharp
:ensure t
:defer t
:init
(add-hook 'csharp-mode-hook 'omnisharp-mode)
:config
(setq omnisharp-root-dir
(expand-file-name "~/Fakespace/omnisharp-server"))
(setq omnisharp-server-executable-path
(expand-file-name "OmniSharp/bin/Debug/" omnisharp-root-dir)))
LaTeX editing for my files
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 obviously
(use-package company-auctex
:ensure t
:after auctex
:config
(company-auctex-init))
Something similar but for spelling
(fn/use-feature flyspell-auctex
(flyspell auctex)
(add-hook 'latex-mode-hook #'flyspell-mode))
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 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 "-"))
Anything that just does something useful
Some redis support
(use-package eredis
:ensure t
:defer t
:commands (eredis-connect)
:config
(setq main (eredis-connect "localhost" 6379)))
Web browsing is a must
(fn/use-executables w3m-check
(w3m)
(use-package w3m
:ensure t
:unless noninteractive
:defer t
: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
(lexical-let* ((url-parts (w3m-parse-http-url url))
(url-host (elt url-parts 1)))
(cl-some
(lambda (pair)
(pcase-let ((`(,name ,search-url) pair))
(lexical-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
(lexical-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)
(fn/use-feature w3m-mode--mode-icon
(all-the-icons)
(fn/add-major-mode-icon
'w3m-mode
(list 'all-the-icons-faicon "globe" :v-adjust -0.1)))))
I want w3m to use tor at least.
(with-eval-after-load 'w3m
(fn/use-executables w3m-privacy-proxy
(w3m tor polipo privoxy)
(defcustom fn/w3m-polipo-cache-dir (expand-file-name "polipo-cache" fn/cache-dir)
"Polipo cache directory."
:type 'directory)
(defcustom fn/w3m-tor-cache-dir (expand-file-name "tor-cache" fn/cache-dir)
"Tor cache directory."
:type 'directory)
(defcustom fn/w3m-privoxy-cache-dir (expand-file-name "polipo-cache" fn/cache-dir)
"Privoxy cache directory."
:type 'directory)
(defcustom fn/w3m-privoxy-conf-dir (expand-file-name "privoxy" fn/system-dir)
"Privoxy conf directory."
:type 'directory)
(defcustom fn/w3m-tor-port 18050
"Tor port"
:type 'number)
(defcustom fn/w3m-polipo-port 18123
"Polipo port"
:type 'number)
(defcustom fn/w3m-privoxy-port 18118
"Polipo port"
:type 'number)
(defcustom fn/w3m-polipo-conf-file (expand-file-name "polipo-w3m-conf" fn/cache-dir)
"Polipo configuration."
:type 'file)
(defcustom fn/w3m-tor-conf-file (expand-file-name "tor-w3m-conf" fn/cache-dir)
"Tor configuration."
:type 'file)
(defcustom fn/w3m-privoxy-conf-file (expand-file-name "privoxy-w3m-conf" fn/cache-dir)
"Privoxy configuration."
:type 'file)
(defun fn/w3m-privacy-update-conf ()
"Create/update `fn/w3m-polipo-conf-file' and `fn/w3m-tor-conf-file' with new
configurations."
(interactive)
(make-directory fn/w3m-polipo-cache-dir t)
(make-directory fn/w3m-tor-cache-dir t)
(make-directory fn/w3m-privoxy-cache-dir t)
(make-directory fn/w3m-privoxy-conf-dir t)
(fn/prodigy-create-polipo-conf
fn/w3m-polipo-conf-file
`(("proxyAddress" . "127.0.0.1")
("allowedClients" . "127.0.0.1")
("diskCacheRoot" . ,fn/w3m-polipo-cache-dir)
("proxyPort" . ,fn/w3m-polipo-port)
("cacheIsShared" . false)
("socksParentProxy" .
,(format "%s:%s" "127.0.0.1" fn/w3m-tor-port))
("socksProxyType" . socks5)
("relaxTransparency" . true)
("logSyslog" . false)
("logLevel" . 0xFF)
("logFile" . "")
;; Optional Security Setting
("localDocumentRoot" . "")
("disableLocalInterface" . true)
("disableConfiguration" . true)
("dnsUseGethostbyname" . yes)
("disableVia" . true)
("censorReferer" . maybe)
("maxConnectionAge" . 5m)
("maxConnectionRequests" . 120)
("serverMaxSlots" . 8)
("serverSlots" . 2)))
(fn/prodigy-create-tor-conf
fn/w3m-tor-conf-file
`(("SocksPort" . ,fn/w3m-tor-port)
("DataDirectory" . ,fn/w3m-tor-cache-dir)
("ControlPort" . ,(1+ fn/w3m-tor-port))
("DisableDebuggerAttachment" . 0)
("Log" . "info")))
(fn/prodigy-create-privoxy-conf
fn/w3m-privoxy-conf-file
`(("confdir" . ,fn/w3m-privoxy-conf-dir)
("logdir" . ,fn/w3m-privoxy-cache-dir)
("actionsfile" . "match-all.action")
("actionsfile" . "default.action")
("actionsfile" . "user.action")
("filterfile" . "default.filter")
("debug" . 13313)
("logfile" . "logfile")
("listen-address" .
,(format "%s:%s" "127.0.0.1" fn/w3m-privoxy-port))
("forward" .
,(format "/ %s:%s" "127.0.0.1" fn/w3m-polipo-port))
("toggle" . 1)
("enable-remote-toggle" . 0)
("enable-edit-actions" . 0)
("enforce-blocks" . 0)
("buffer-limit" . 4096)
("enable-proxy-authentication-forwarding" . 0)
("forwarded-connect-retries" . 0)
("accept-intercepted-requests" . 0)
("allow-cgi-request-crunching" . 0)
("split-large-forms" . 0)
("keep-alive-timeout" . 5)
("tolerate-pipelining" . 1)
("socket-timeout" . 300))))
(defun fn/w3m-remove-http-proxy-arguments ()
"Remove http_proxy= from `w3m-command-arguments'."
(setq w3m-command-arguments ;; Remove added http_proxy arguments
(cl-reduce ;; I wish this could be easier
(lambda (val xs)
(if (and (string= "-o" val)
(or
(string-prefix-p "http_proxy="
(or (car xs) ""))
(string-prefix-p "https_proxy="
(or (car xs) ""))))
(cdr xs)
(cons val xs)))
w3m-command-arguments
:from-end t
:initial-value (list))))
(defun fn/w3m-append-http-proxy-arguments ()
"Add http_proxy= to `w3m-command-arguments' while removing previous ones."
(fn/w3m-remove-http-proxy-arguments)
(setq w3m-command-arguments
(append w3m-command-arguments
(list "-o"
(format
"http_proxy=http://127.0.0.1:%s/"
fn/w3m-privoxy-port))
(list "-o"
(format
"https_proxy=https://127.0.0.1:%s/"
fn/w3m-privoxy-port)))))
(require 'prodigy)
(require 'prodigy-set)
(defun fn/w3m-service-start (&rest args)
"Add http-proxy from `w3m-command-arguments'."
(when (prodigy-set-started-p fn/w3m-privacy-set)
(fn/w3m-append-http-proxy-arguments)))
(defun fn/w3m-service-stop (&rest args)
"Add http-proxy from `w3m-command-arguments'."
(fn/w3m-remove-http-proxy-arguments))
(defun fn/w3m-service-init (&rest args)
"If both service are stopped, update config before starting."
(unless (prodigy-set-started-p fn/w3m-privacy-set)
(fn/w3m-privacy-update-conf)))
(prodigy-define-tag
:name 'w3m
:init #'fn/w3m-service-init
:on-start #'fn/w3m-service-start
:on-stop #'fn/w3m-service-stop)
(defconst fn/w3m-polipo-service-name "w3m-polipo"
"Polipo prodigy service name.")
(defconst fn/w3m-tor-service-name "w3m-tor"
"Tor prodigy service name.")
(defconst fn/w3m-privoxy-service-name "w3m-privoxy"
"Privoxy prodigy service name.")
(fn/prodigy-define-service
:name fn/w3m-polipo-service-name
:tags '(w3m polipo)
:port fn/w3m-polipo-port
:args (list "-c" fn/w3m-polipo-conf-file))
(fn/prodigy-define-service
:name fn/w3m-tor-service-name
:tags '(w3m tor)
:port fn/w3m-tor-port
:args (list "-f" fn/w3m-tor-conf-file))
(fn/prodigy-define-service
:name fn/w3m-privoxy-service-name
:tags '(w3m privoxy)
:port fn/w3m-privoxy-port
:args (list "--no-daemon" fn/w3m-privoxy-conf-file))
(defconst fn/w3m-privacy-set
(prodigy-set-define-set
:name "w3m-privacy"
:strategy 'sequential
:services `(("w3m-privoxy" . ready)
("w3m-polipo" . ready)
("w3m-tor" . ready)))
"My privacy set.")
(prodigy-set-mode t)
(defun fn/w3m-start-privacy-proxies (&rest args)
"Start services"
(interactive)
(prodigy-set-mode t)
(prodigy-set-start-set fn/w3m-privacy-set))
(defun fn/w3m-kill-privacy-proxies (&rest args)
"Stop special services."
(interactive)
(prodigy-set-mode t)
(prodigy-set-stop-set fn/w3m-privacy-set))
(add-to-list 'w3m-no-proxy-domains "127.0.0.1")
(add-to-list 'w3m-no-proxy-domains "localhost")
(defun fn/w3m-add-current-host-to-no-proxy-domains ()
"Add current host to `w3m-no-proxy-domains'"
(interactive)
(when (eq major-mode 'w3m-mode)
(lexical-let* ((parts (w3m-parse-http-url w3m-current-url))
(host (elt parts 1)))
(add-to-list 'w3m-no-proxy-domains host t)
(w3m-reload-this-page))))
(when (yes-or-no-p "Start privacy proxies for w3m? ")
(fn/w3m-start-privacy-proxies)
(message "Delaying a little bit after starting the proxies.")
(sleep-for 1))))
Hook up vlc
and w3m
when visiting certain sites
(fn/use-feature w3m-vlc
(w3m)
(fn/use-executables w3m-vlc-check
(vlc)
(require 'w3m-cookie) ;; Required by `w3m-parse-http-url'
(defcustom fn/w3m-video-executable "vlc"
"The executable that can run a network video stream.")
(defcustom fn/w3m-video-args
(list
;; This skews how the process is managed for vlc
;; "--one-instance"
;; "--play-and-exit"
)
"Extra arguments to run `fn/w3m-video-executable'")
(defun fn/w3m-video (&optional url)
"Get image video url."
(or url (w3m-anchor) w3m-current-url))
(defun fn/w3m-view-video ()
"View the video url with `fn/w3m-video-executable'"
(interactive)
(let ((url (w3m-url-valid (fn/w3m-video))))
(if (null url)
(prog1
(w3m-message "No video at point")
nil)
(lexical-let ((video-process
(apply #'start-process
(append
(list
(format "%s-%s" "w3m" fn/w3m-video-executable)
nil
fn/w3m-video-executable)
fn/w3m-video-args
(list url)))))
(with-current-buffer (current-buffer)
(fn/w3m-kill-page-process)
(setq-local fn/w3m-page-process video-process))))))
(defun fn/w3m-video-url-p (url)
"Check if URL is a video."
(ignore-errors
(lexical-let* ((pieces (w3m-parse-http-url url))
(host (elt pieces 1))
(path (elt pieces 3)))
(if (or (and (string= host "www.youtube.com")
(string-prefix-p "/watch" path)))
url nil))))
(defun fn/w3m-view-this-video-external (url)
"View this video externally"
(lexical-let ((video-url (fn/w3m-video-url-p (fn/w3m-video))))
(when
(and video-url
(yes-or-no-p
(format "%s is a video, view it with %s?"
video-url fn/w3m-video-executable)))
(fn/w3m-view-video))))
(defun fn/w3m-kill-page-process ()
"Kill the `fn/w3m-page-process' of the current buffer."
(interactive)
(with-current-buffer (current-buffer)
(when (and (boundp 'fn/w3m-page-process)
(process-live-p fn/w3m-page-process))
(kill-process fn/w3m-page-process))
(setq-local fn/w3m-page-process nil)))
(defun fn/w3m-auto-kill-page-process (url)
"If the page has a process, quit it."
(fn/w3m-kill-page-process))
(defun fn/w3m-single-page-process (result)
"If the page process is a singleton, adjust page container accordingly."
(when result
(lexical-let ((this-page (current-buffer))
(active-process nil))
(mapc
(lambda (page)
(with-current-buffer page
(when (process-live-p fn/w3m-page-process)
(setq active-process fn/w3m-page-process))
(setq-local fn/w3m-page-process nil)))
(w3m-list-buffers))
(with-current-buffer this-page
(setq-local fn/w3m-page-process active-process))))
result)
(add-hook 'w3m-display-hook #'fn/w3m-auto-kill-page-process t)
(add-hook 'w3m-display-hook #'fn/w3m-view-this-video-external t)
(add-hook 'kill-buffer-hook #'fn/w3m-kill-page-process t)
))
Hooking up vlc
and sumy
to aid in reading web pages.
(fn/use-feature w3m-sumy
(w3m)
(fn/use-executables w3m-sumy-check
(sumy)
(require 'deferred)
(defcustom fn/w3m-sumy-executable "sumy"
"The executable that can run a summarize articles.")
(defcustom fn/w3m-sumy-args
(list
"lex-rank"
"--length" "5")
"Extra arguments to run `fn/w3m-sumy-executable'")
(defface fn/w3m-summary '((t (:weight bold :height 1.1 :box (:line-width 2 :color "grey75" :style released-button))))
"Summary face.")
(defun fn/w3m-summarize-url (url)
"Summarize URL for easier comprehension"
(lexical-let ((url url))
(deferred:nextc
(apply
#'deferred:process-shell
(append
(list fn/w3m-sumy-executable)
fn/w3m-sumy-args
(if (string-prefix-p "file://" url)
(list "--file"
(concat "\"" (substring-no-properties url (length "file://")) "\""))
(list "--url"
(concat "\"" url "\"")))))
(lambda (summary-output)
(lexical-let ((processed-text (string-trim-right summary-output)))
(if (string-empty-p processed-text)
nil
(mapcar
(lambda (text) (replace-regexp-in-string "\n" " " text))
(split-string processed-text "\\\.\n"))))))))
(defun fn/w3m-fontify-summary ()
"Fontify summary points."
(interactive)
(when (executable-find fn/w3m-sumy-executable)
(lexical-let ((fontifier
(lambda (text)
(let ((inhibit-read-only t)
(new-text
(string-join
(split-string (regexp-quote text) " ") "\n*\s*")))
(goto-char (point-min))
(while (re-search-forward new-text nil t)
(w3m-add-face-property (match-beginning 0) (match-end 0) 'fn/w3m-summary))))))
(setq-local fn/w3m-fontify-summary (fn/w3m-summarize-url w3m-current-url))
(with-current-buffer (current-buffer)
(message "Summarizing %s" w3m-current-url)
(deferred:nextc
fn/w3m-fontify-summary
(lambda (text-points)
(setq-local fn/w3m-fontify-summary nil)
(if (null text-points)
(message "No summary for %s" (w3m-current-title))
(message "Summarizing points on %s" w3m-current-url)
(save-excursion
(mapc fontifier text-points))
(message "Summary on %s:\n%s"
(w3m-current-title)
(string-join
(mapcar
(lambda (text-point)
(format "* %s" text-point))
text-points)
"\n")))))))))
(defun fn/w3m-kill-page-summarizer ()
"Kill page summarizer."
(when (and (boundp 'fn/w3m-page-summarizer)
fn/w3m-page-summarizer
(deferred-p fn/w3m-page-summarizer))
(deferred:cancel fn/w3m-page-summarizer))
(setq-local fn/w3m-page-summarizer nil))
(defcustom fn/w3m-summarize-ignore-page-functions (list)
"Functions that check if the url should be summarized.
Each function takes an current URL argument and returns non-nil
when the page should not be summarized."
:type 'hook)
(add-to-list 'fn/w3m-exclude-ignore-search-engine "wikipedia")
(add-to-list 'fn/w3m-exclude-ignore-search-engine "youtube")
(add-hook 'fn/w3m-summarize-ignore-page-functions #'fn/w3m-ignore-search-engine-pages)
(add-hook 'fn/w3m-summarize-ignore-page-functions #'fn/w3m-ignore-special-pages)
(defun fn/w3m-summarize-page ()
"Summarize page using `sumy' by checking `fn/w3m-summarize-ignore-page-functions'."
(unless (run-hook-with-args-until-success 'fn/w3m-summarize-ignore-page-functions w3m-current-url)
(fn/w3m-fontify-summary)))
(add-hook 'w3m-fontify-before-hook #'fn/w3m-kill-page-summarizer)
(add-hook 'w3m-fontify-after-hook #'fn/w3m-summarize-page)))
At attempt to zone out while waiting to render
(fn/use-feature w3m-zone
(w3m)
(defun fn/w3m-zone-retrieve (&rest args)
(with-current-buffer (current-buffer)
(let ((inhibit-read-only t))
(erase-buffer)
(goto-char (point-min)))
(setq-local fn/w3m-zone-timer
(run-at-time
(/ 10.0)
(/ 30.0)
(lambda ()
(let ((inhibit-read-only t)
(increment (- 6 (random 12))))
(unless (boundp 'fn/w3m-zone-counter)
(setq-local fn/w3m-zone-counter (+ 20 (random 20))))
(setq-local fn/w3m-zone-counter
(+ fn/w3m-zone-counter increment))
(setq-local fn/w3m-zone-counter (max fn/w3m-zone-counter 1))
(insert
(string-join
(mapcar
(lambda (ch)
(cond
((< 0 increment) "\\")
((> 0 increment) "/")
(t "-")))
(number-sequence 1 fn/w3m-zone-counter))
""))
(insert "\n")))))))
;; (advice-add 'w3m-retrieve :after #'fn/w3m-zone-retrieve)
(defun fn/w3m-cancel-zone-render (&rest args)
(when (and (boundp 'fn/w3m-zone-timer)
(timerp fn/w3m-zone-timer))
(cancel-timer fn/w3m-zone-timer))
(setq-local fn/w3m-zone-counter (+ 20 (random 20)))
(setq-local fn/w3m-zone-timer nil))
;; (advice-add 'w3m-rendering-buffer :before #'fn/w3m-cancel-zone-render)
)
Auto bookmark pages when I seem to visit it frequently
(fn/use-feature w3m-auto-bookmarking
(w3m)
(defcustom fn/w3m-auto-bookmark-check-functions (list)
"Functions that check if a function should be auto-bookmarked.
Each function takes the same arguments as `w3m-arrived-add' and
returns non-nil when the url is bookmarked."
:type 'hook)
(defcustom fn/w3m-auto-bookmark-handler-functions (list)
"Functions that handle how a url should be bookmarked.
Each function takes the same arguments as `w3m-arrived-add' and
returns a cons pair where the car is the section and cdr the title
if it handles it. "
:type 'hook)
(defvar fn/w3m-url-visit-table (make-hash-table :test 'equal)
"Record the number of times an url is visited.")
(defcustom fn/w3m-auto-bookmark-frequent-visit-count 3
"Number of times a site should be visited before being bookmarked.")
(defun fn/w3m-auto-bookmark-frequently-visited (url title)
"Check if url is visited frequently enough."
(when (eq (gethash url fn/w3m-url-visit-table 'nothing) 'nothing)
(puthash url 0 fn/w3m-url-visit-table))
(lexical-let ((counter (gethash url fn/w3m-url-visit-table)))
(unless (/= -1 counter)
(puthash url (1+ ) fn/w3m-url-visit-table)))
(lexical-let ((result (>= (gethash url fn/w3m-url-visit-table) fn/w3m-auto-bookmark-frequent-visit-count)))
(prog1
result
(when result
(puthash url -1 fn/w3m-url-visit-table)))))
(add-hook 'fn/w3m-auto-bookmark-check-functions #'fn/w3m-auto-bookmark-frequently-visited)
(defcustom fn/w3m-auto-bookmark-host-handlers (list)
"Handle bookmarking by its host.
This is a list of cons where the car is the host of the url and
cdr a function that takes a url and title argument and returns a
cons pair as specified by
`fn/w3m-auto-bookmark-handler-functions'."
:type 'list)
(defun fn/w3m-auto-bookmark-host-handler (url title)
"Handle bookmarking by host as specified `fn/w3m-auto-bookmark-handler'."
(lexical-let* ((pieces (w3m-parse-http-url url))
(host (elt pieces 1))
(host-handler (cdr (assoc host fn/w3m-auto-bookmark-host-handlers))))
(if host-handler
(funcall host-handler url title)
nil)))
(progn
(require 's)
(add-to-list 'fn/w3m-auto-bookmark-host-handlers
(cons "github.com"
(lambda (url title)
(cons "Github"
(cadr
(s-match "GitHub - \\(.*\\): .*"
title)))))))
(add-hook 'fn/w3m-auto-bookmark-handler-functions 'fn/w3m-auto-bookmark-host-handler)
(defun fn/w3m-bookmarked-p (url)
"Check if URL is already bookmarked."
(with-current-buffer (find-file-noselect w3m-bookmark-file)
(goto-char (point-min))
(search-forward url nil t)))
(defun fn/w3m-auto-bookmark-arrived-url ()
"Check whether to auto bookmark a page."
(when (w3m-arrived-get url 'title) ;; Check if url is not private
(lexical-let* ((url w3m-current-url)
(title w3m-current-title))
(when (and (not (fn/w3m-bookmarked-p url))
(run-hook-with-args-until-success
'fn/w3m-auto-bookmark-check-functions
url title))
(lexical-let ((pair (run-hook-with-args-until-success
'fn/w3m-auto-bookmark-handler-functions
url title)))
(when (and pair (consp pair))
(pcase-let ((`(,section . ,title) pair))
(when (yes-or-no-p
(format "Bookmark %s on %s as %s? " url section title ))
(w3m-bookmark-write-file
url
(w3m-encode-specials-string title)
(w3m-encode-specials-string section))))))))))
(add-hook 'w3m-fontify-after-hook #'fn/w3m-auto-bookmark-arrived-url))
Integrating with emacs is very fun via twittering
(use-package twittering-mode
:ensure t
:defer t
:commands (twittering-mode)
:init
(setq twittering-auth-method 'oauth)
(setq twittering-use-master-password t)
:config
(twittering-icon-mode t)
(setq twittering-convert-fix-size 24))
Connecting with the brothers
(use-package erc
:ensure t
:defer t
:bind (:map fn-standard-prefix-map
("e q" . erc-quit-server)
("e e" . erc))
:config
(setq erc-try-new-nick-p t
;; Setup for server and such is in personal.el
erc-prompt-for-password nil
erc-rename-buffers t
erc-interpret-mirc-color t
erc-quit-reason (lambda (&rest args) "I took with me... their lightning and their prayers"))
(fn/use-feature erc-message-alert
(alert)
(require 's)
(defun fn/erc-notice-p (message)
"Check if MESSAGE is a notice."
(s-starts-with-p erc-notice-prefix message))
(defun fn/erc-get-sender-name (message)
"Get the sender from the MESSAGE."
(let ((result (car (s-split-up-to " " message 1))))
(if (string= result message)
nil
(substring-no-properties result 1 -1))))
(defun fn/erc-get-message (message)
"Get the message from the MESSAGE."
(let ((result (cadr (s-split-up-to " " message 1))))
(if (null result)
nil
result)))
(defun fn/erc-message-alert (message)
"Just a small unobtrusive notifcation"
(ignore-errors ;; NOTE: I shall return
(let ((raw-message (string-trim-right (substring-no-properties message))))
(when (and (erc-network-name)
(erc-default-target)
(not (erc-is-message-ctcp-p raw-message))
(not (fn/erc-notice-p raw-message)))
(let ((server (erc-network-name))
(channel (substring-no-properties (erc-default-target) 1))
(chatter (fn/erc-get-sender-name raw-message))
(base-message (fn/erc-get-message raw-message)))
(fn/alert-color (fn/chat-log server channel chatter base-message)
:color "#3d57b1"
:category 'erc))))))
(add-hook 'erc-insert-pre-hook #'fn/erc-message-alert))
(fn/use-feature erc-mode--mode-icon
(all-the-icons)
(fn/add-major-mode-icon
'erc-mode
(list 'all-the-icons-faicon "exchange" :v-adjust 0.0))))
Listen to chat notifications
(use-package jabber
:ensure t
:bind (:map fn-standard-prefix-map
("J J" . jabber-connect-all)
("J c" . jabber-chat-with)
("J q" . jabber-disconnect))
:config
;; account set at personal.el
;; core
(setq jabber-auto-reconnect t
jabber-version-show nil
jabber-history-enabled t
jabber-use-global-history nil
jabber-history-dir (expand-file-name "jabber-history/" fn/cache-dir)
jabber-backlog-number 40
jabber-backlog-days 30)
(defconst fn/jabber-roster-buffer-name "*-jabber-*"
"My custom name for the jabber buffer")
;; chat
(setq jabber-chat-buffer-format "*-jabber-%n-*")
;; history
(setq jabber-history-enabled t
jabber-history-dir (expand-file-name "jabber-history" fn/cache-dir))
;; mode
(setq jabber-mode-line-mode t)
;; chat states
(setq jabber-chatstates-confirm nil
jabber-presence-hooks nil)
;; avatar
(setq jabber-avatar-cache-directory (expand-file-name "jabber-avatar-cache" fn/cache-dir)
jabber-vcard-avatars-publish nil
jabber-vcard-avatars-retrieve nil)
;; libnotify
(setq jabber-libnotify-method 'shell
jabber-libnotify-urgency "high")
;; presence
(setq jabber-alert-presence-message-function nil)
;; chat
(setq jabber-chat-foreign-prompt-format "[%t] %n>"
jabber-chat-local-prompt-format "[%t] %n>"
jabber-groupchat-prompt-format "[%t] %n>"
jabber-muc-private-foreign-prompt-format "[%t] %g/%n>")
;; alerts
(setq jabber-alert-message-hooks nil)
(fn/use-feature jabber-alert-custom
(alert)
(defconst fn/jabber-google
(format "%s%s%s%s%s%s"
(propertize "g" 'face '(:foreground "#4285f4"))
(propertize "o" 'face '(:foreground "#ea4335"))
(propertize "o" 'face '(:foreground "#fbbc05"))
(propertize "g" 'face '(:foreground "#4285f4"))
(propertize "l" 'face '(:foreground "#34a853"))
(propertize "e" 'face '(:foreground "#ea4335")))
"A nice candy colored google text")
(defun fn/jabber-message-notify (from buffer text title)
"Just a small unobtrusive notifcation"
(let ((chatter (cdr (assoc from jabber-activity-name-alist))))
(fn/alert-color
(fn/chat-log fn/jabber-google user-mail-address chatter text)
:color "#abb2e1"
:category 'jabber)))
(add-hook 'jabber-alert-message-hooks #'fn/jabber-message-notify))
(fn/use-feature jabber-roster-mode--mode-icon
(all-the-icons)
(fn/add-major-mode-icon
'jabber-roster-mode
(list 'all-the-icons-octicon "radio-tower" :v-adjust -0.1)))
(fn/use-feature jabber-chat-mode--mode-icon
(all-the-icons)
(fn/add-major-mode-icon
'jabber-chat-mode
(list 'all-the-icons-faicon "comments" :v-adjust -0.1)))
;; roster
(setq jabber-roster-show-title nil
jabber-roster-line-format "%c %-25n %u %-8s %S"
jabber-roster-buffer fn/jabber-roster-buffer-name)
(defun fn/display-jabber-roster-buffer ()
"Show jabber roster buffer"
(interactive)
(display-buffer-same-window (get-buffer fn/jabber-roster-buffer-name) nil)))
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))
(fn/use-feature slack-message-flyspell
(flyspell slack)
(add-hook 'slack-edit-message-mode-hook #'flyspell-mode))
(fn/use-feature slack-custom-alert
(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))
(fn/use-feature slack-mode--mode-icon
(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)))
Stack Exchange api
(use-package sx
:bind (:map fn-standard-prefix-map
("S q" . sx-tab-all-questions)
("S i" . sx-inbox)
("S o" . sx-open-link)
("S u" . sx-tab-unanswered-my-tags)
("S a" . sx-ask)
("S s" . sx-search))
:config
(setq sx-cache-directory (expand-file-name ".sx" fn/cache-dir)))
People organization
(use-package bbdb
:ensure t
:defer t
:config
(bbdb-initialize)
(add-hook 'gnus-startup-hook 'bbdb-insinuate-gnus))
This controls the music player.
(defconst fn/emms-map (fn/make-prefixed-keymap (kbd "C-p") fn-standard-prefix-map)
"My custom prodigy map.")
(fn/use-executables emms-player-check
(vlc mpd)
(use-package emms
:ensure t
: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))
(fn/use-executables emms-mp3info
(mp3info)
(add-to-list 'emms-info-functions 'emms-info-mp3info))
(fn/use-executables emms-ogginfo
(ogginfo)
(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))))
I prefer mpd
as a music player as it is more stable.
(fn/use-executables mpd-check
(mpd)
(fn/use-feature emms-mpd
(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))
(fn/use-feature prodigy-emms-mpd
(prodigy emms)
(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))))
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)
Bootstrap different configurations of Emacs for reviewing expert configurations and trying different modes
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))
Load another copy of this configuration for vanity sake I suppose?
(defun fn/bootstrap-itself ()
(interactive)
(fn/bootstrap-new-emacs nil))
Just a random build for myself
(setq fn/experimental-home "~/.fmacs.d/")
(defun fn/bootstrap-experimental ()
(interactive)
(fn/bootstrap-new-emacs fn/experimental-home))
The love child of Emacs and Vim, what does it hold?
(setq fn/spacemacs-home "~/Modules/.spacemacs.d/")
(defun fn/bootstrap-spacemacs ()
(interactive)
(fn/bootstrap-new-emacs fn/spacemacs-home))
I don’t need to tangle the whole file. Maybe just one block if I can.
(require 'f)
(random t)
(defun fn/random-uuid ()
;; Thanks to http://ergoemacs.org/emacs/elisp_generate_uuid.html
"Generate a random UUID.
Example of a UUID: 1df63142-a513-c850-31a3-535fc3520c3d
WARNING: this is a simple implementation. The chance of generating the same UUID is much higher than a robust algorithm."
(interactive)
(format "%04x%04x-%04x-%04x-%04x-%06x%06x"
(random (expt 16 4))
(random (expt 16 4))
(random (expt 16 4))
(random (expt 16 4))
(random (expt 16 4))
(random (expt 16 6))
(random (expt 16 6))))
(defconst fn/tangled-config-file (f-swap-ext fn/config-file "el")
"The tangled config file.")
(defun fn/get-code-block-id ()
"Get code block id at point."
(interactive)
(cdr (assoc fn/code-block-id-symbol (nth 2 (org-babel-get-src-block-info)))))
(defun fn/get-code-block-previous-id ()
"Get previous code block id at point."
(interactive)
(condition-case ex
(save-excursion
(org-babel-goto-src-block-head)
(org-babel-previous-src-block)
(fn/get-code-block-id))
('error nil)))
(defun fn/get-code-block-next-id ()
"Get next code block id at point."
(interactive)
(condition-case ex
(save-excursion
(org-babel-goto-src-block-head)
(org-babel-next-src-block)
(fn/get-code-block-id))
('error nil)))
(defun fn/get-code-block-code ()
"Get src code at point."
(interactive)
(nth 1 (org-babel-get-src-block-info)))
(defun fn/generate-code-block-id ()
"Generate code block id for block."
(interactive)
(if (fn/get-code-block-id)
nil
(save-excursion
(org-babel-goto-src-block-head)
(end-of-line)
(insert " ")
(let ((id (symbol-name fn/code-block-id-symbol)))
(insert
(format "%s %s"
id
(fn/random-uuid)))
id))))
(defun fn/generate-code-block-ids (&optional buffer)
"Generate block ids for current BUFFER."
(interactive)
(save-excursion
(beginning-of-line)
(condition-case ex
(while t
(org-babel-next-src-block)
(fn/generate-code-block-id))
('error nil))))
(defun fn/find-code-block (&optional block-id)
"Find code block with equivalent BLOCK-ID."
(interactive)
(let ((id (or block-id
(fn/get-code-block-id))))
(unless id
(error "No code block id."))
(let ((block-start (format fn/code-block-start-format id)))
(condition-case ex
(with-current-buffer (find-file fn/tangled-config-file)
(goto-char (point-min))
(search-forward block-start)
(beginning-of-line)
(switch-to-buffer (current-buffer)))
('error (message "Cannot find block id: %s" (error-message-string ex)))))))
(defun fn/update-code-block (&optional block-id)
"Update or add code block with equivalent BLOCK-ID."
(interactive)
(let ((id (or block-id
(fn/get-code-block-id)))
(code (fn/get-code-block-code)))
(let ((block-start (format fn/code-block-start-format id))
(block-end (format fn/code-block-end-format id))
(tangle-buffer (find-file-noselect fn/tangled-config-file)))
(cond
((condition-case ex
(with-current-buffer tangle-buffer
(beginning-of-buffer)
(search-forward block-start)
(beginning-of-line)
(forward-line)
(command-execute #'set-mark-command)
(search-forward block-end)
(beginning-of-line)
(kill-region (region-beginning) (region-end))
(open-line 1)
(command-execute #'set-mark-command)
(insert code)
(insert "\n")
(save-buffer)
t)
('error nil))
'update)
((condition-case ex
(let* ((previous-id (fn/get-code-block-previous-id))
(previous-block-end (format fn/code-block-end-format previous-id)))
(with-current-buffer tangle-buffer
(beginning-of-buffer)
(search-forward previous-block-end)
(beginning-of-line)
(next-line)
(insert "\n")
(insert block-start)
(insert "\n")
(insert code)
(insert "\n")
(insert block-end)
(insert "\n")
(insert "\n")
(save-buffer)
t))
('error nil))
'add)
((condition-case ex
(let* ((next-id (fn/get-code-block-next-id))
(next-block-start (format fn/code-block-start-format next-id)))
(with-current-buffer tangle-buffer
(beginning-of-buffer)
(search-forward next-block-start)
(beginning-of-line)
(previous-line)
(insert "\n")
(insert block-start)
(insert "\n")
(insert code)
(insert "\n")
(insert block-end)
(insert "\n")
(insert "\n")
(save-buffer)
t))
('error nil))
'add)
(t
(error "Trouble of updating code block"))))))
(defun fn/check-duplicate-code-block-id ()
"Check current buffer for duplicate ids."
(interactive)
(let ((id-table (make-hash-table))
(same-ids (list))
(current-id))
(save-excursion
(beginning-of-buffer)
(condition-case ex
(while t
(org-babel-next-src-block)
(setq current-id (fn/get-code-block-id))
(if (gethash current-id id-table nil)
(push current-id same-ids)
(puthash current-id t id-table)))
('error nil)))
same-ids))
(defcustom fn/org-edit-src-exit-hook nil
"Hook when running edit exit."
:type 'hook
:group 'fn)
(defun fn/org-edit-src-exit-hook-run (&rest args)
"Run `fn/org-edit-src-exit-hook'."
(run-hooks 'fn/org-edit-src-exit-hook))
(advice-add 'org-edit-src-exit :after #'fn/org-edit-src-exit-hook-run)
Split the windows into a nice four panel division. This works even well with golden-ratio.
(require 'dash)
(defconst fmw/empty-buffer "*unchained buffer*"
"A buffer to represent an empty one")
(defun fmw/remap-window-buffers (buffer-remapper)
"Reassigns the windows with new buffers taking a remapper function over the old ones"
(lexical-let* ((current-buffers (mapcar 'window-buffer (window-list)))
(empty-buffer (if (get-buffer fmw/empty-buffer)
(get-buffer fmw/empty-buffer)
(generate-new-buffer fmw/empty-buffer)))
(new-buffers (funcall buffer-remapper current-buffers)))
(mapc (lambda (pair)
(pcase-let ((`(,current-buffer . ,current-window) pair))
(when current-window
(set-window-buffer current-window
(if current-buffer current-buffer
empty-buffer))))
)
(-zip-fill nil new-buffers (window-list)))))
(defun fmw/restore-buffers (window-movement)
"Restore buffers after doing a window layout"
(lexical-let ((mover window-movement))
(fmw/remap-window-buffers (lambda (old-buffers)
(funcall window-movement)
old-buffers))))
(defun fmw/rotate-window-buffers (arg)
"Rotate the buffers from window to the next like a clock"
(interactive "P")
(lexical-let ((rotation (if arg arg 1)))
(fmw/remap-window-buffers (lambda (old-buffers)
(-rotate rotation old-buffers)))))
(defun fmw/four-part-horizontal-window-layout ()
"Split the frame into four windows horizontally. I use this when I'm working in a project where the
- upper left serves the main file
- upper right serves as a secondary file
- lower left serves as auxilliary or task buffer
- lower right serves a project buffer"
(interactive)
(fmw/restore-buffers (lambda ()
(setq golden-ratio-mode t)
(delete-other-windows)
(split-window-vertically)
(split-window-horizontally)
(other-window 2)
(split-window-horizontally)
(other-window 2))))
(defun fmw/four-part-vertical-window-layout ()
"Unlike with the =fn/four-way-horizontal-layout=, I use this more oftenly on dired to make file moving easier
(and probably more)"
(interactive)
(fmw/restore-buffers (lambda ()
(setq golden-ratio-mode t)
(delete-other-windows)
(split-window-vertically)
(split-window-horizontally)
(other-window 2)
(split-window-horizontally)
(other-window 2))))
(defun fmw/three-part-left-window-layout ()
"Like =fn/four-way-horizontal-layout= but fits a smaller screen namely my Raspberry Pi."
(interactive)
(fmw/restore-buffers (lambda ()
(setq golden-ratio-mode t)
(delete-other-windows)
(split-window-horizontally)
(other-window 1)
(split-window-vertically)
(other-window 2))))
(defun fmw/three-part-vertical-window-layout ()
"An layout seen at a meetup, seems interesting to work with.
However, this is disables golden-ration-mode to maintain the delicate origami folds"
(interactive)
(fmw/restore-buffers (lambda ()
(setq golden-ratio-mode nil)
(delete-other-windows)
(lexical-let* ((total-width (frame-width))
(one-third-fold (/ total-width 3)))
;; Split the right window by a third, negative arguments huh.
(split-window-horizontally (- one-third-fold))
(split-window-horizontally (- one-third-fold))))))
(defun fmw/four-part-equal-vertical-window-layout ()
"This is for my music conversion but not that mind blowing to do"
(interactive)
(fmw/restore-buffers (lambda ()
(setq golden-ratio-mode nil)
(delete-other-windows)
(split-window-horizontally)
(split-window-horizontally)
(other-window 2)
(split-window-horizontally)
(other-window 2))))
Some commands to get me here and there
(defun fn/insert-date ()
"Insert time at point, useful when you want to put a date.
TODO: Would be useful if you can change formats too"
(interactive)
(let ((time-format "%Y-%m-%d"))
(insert (format-time-string time-format))))
(defun fn/save-and-kill-buffer ()
"Save and kill the buffer in one command"
(interactive)
(call-interactively 'save-buffer)
(call-interactively 'kill-this-buffer))
(defun fn/dired-parent ()
"Quick command to go to parent dired of buffer immediately"
(interactive)
(dired ".."))
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))
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"))
Check if Emacs is running fast.
(run-at-time nil 60 #'force-mode-line-update)
(fn/use-feature frame-delay
nil
(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!?
(require 'deferred)
(fn/use-executables cpu-check
(grep awk)
(fn/use-feature cpu-usage
(deferred)
(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)))
(fn/use-executables memory-check
(free grep awk)
(fn/use-feature memory-usage
(deferred)
(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)))
(fn/use-executables battery-check
(acpi awk)
(fn/use-feature battery-usage
(deferred)
(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))))
A personal script to bundle my journal, recordings and images into one org file. Not to be consumed by anyone else
(require 's)
(defun fmd/get-document-property (key &optional selected-buffer)
"Get the property of the document which starts with #+PROPERTY"
(with-current-buffer (if selected-buffer selected-buffer (buffer-name))
(lexical-let* ((property-regex (format "\#\\+%s:\s+\\(.*\\)" key))
(property-match (s-match property-regex (buffer-substring-no-properties (point-min) (point-max))))
(property (if property-match (cadr property-match)
nil)))
property)))
(defun fmd/get-document-block-property (key &optional selected-buffer)
"Like with fmd/get-document-property but finds a block summary #+BEGIN_key #+END_key"
(with-current-buffer (if selected-buffer selected-buffer (buffer-name))
(lexical-let* ((property-regex (format "#\\+BEGIN_%s\n\\([\0-\377[:nonascii:]]*\\)\n#\\+END_%s" key key))
(property-match (s-match property-regex (buffer-substring-no-properties (point-min) (point-max))))
(property (if property-match (s-trim (cadr property-match))
nil)))
property)))
(defun fmd/get-title (&optional selected-buffer)
"Get the #+TITLE value of the current buffer"
(fmd/get-document-property "TITLE" selected-buffer))
(defun fmd/get-summary (&optional selected-buffer)
"Get the #+BEGIN_SUMMARY section of the buffer"
(fmd/get-document-block-property "SUMMARY" selected-buffer))
(defconst fmd/md-root "~/Mountain/diary/media-diary/compile/"
"The media diary directory")
(defconst fmd/md-compile-date-filter (expand-file-name ".date" fmd/md-root)
"The media diary directory")
(defconst fmd/md-summary-file-name "media-diary.org"
"The media diary directory")
(defconst fmd/md-summary-file (expand-file-name "media-diary.org" fmd/md-root)
"The media diary directory")
(defconst fmd/md-title "Media Diary"
"Main header of the file")
(defconst fmd/md-note-title "Notes"
"Note header of the file")
(defconst fmd/md-journal-title "Journal"
"Journal header of the file")
(defconst fmd/md-media-title "Media"
"Journal header of the file")
(defconst fmd/md-datetime-format "%b. %d, %Y"
"Time format for the compilation")
(defconst fmd/md-audio-datetime-format "%b. %d, %Y %T : Recording"
"Audio time format for the compilation")
(defconst fmd/md-photo-datetime-format "%b. %d, %Y %T : Photograph"
"Photo format for the compilation")
(defconst fmd/md-summary-default-text "Something profound happened but I forgot like Dory"
"The default text to be displayed")
(defconst fmd/md-journal-summary-default-text "Looks like I forgot to write something this day"
"The default text to be displayed")
(defconst fmd/md-journal-dir-name "journal"
"The journal directory source")
(defconst fmd/md-audio-dir-name "audio"
"The audio directory source")
(defconst fmd/md-photo-dir-name "photo"
"The photo directory source")
(defconst fmd/md-photo-extension "jpg"
"The photo file extension")
(defconst fmd/md-audio-extension "mp3"
"The audio file extension")
(defconst fmd/md-journal-dir (expand-file-name fmd/md-journal-dir-name fmd/md-root)
"The journal directory")
(defconst fmd/md-audio-dir (expand-file-name fmd/md-audio-dir-name fmd/md-root)
"The audio directory")
(defconst fmd/md-photo-dir (expand-file-name fmd/md-photo-dir-name fmd/md-root)
"The audio directory")
(defun fmd/parse-file-parts (file)
"Parse the file name compatible for the media diary file"
(lexical-let* ((file-regex "\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}\\)?_?\\([0-9]\\{2\\}-[0-9]\\{2\\}-[0-9]\\{2\\}+\\)?\\( - \\)?\\(.+\\)?\\.\\(.+\\)")
(filename (file-name-nondirectory file))
(file-matches (s-match-strings-all file-regex filename))
(file-match (car file-matches)))
(message filename)
(lexical-let* ((file-date (nth 1 file-match))
(file-time (nth 2 file-match))
(file-title (nth 4 file-match))
(file-extension (nth 5 file-match)))
(lexical-let* ((parts (list))
(time-part (if file-time (s-replace "-" ":" file-time)
"00:00:00"
))
(date-raw (concat file-date " " time-part))
(datetime-part (apply 'encode-time (parse-time-string date-raw))))
(setq parts (plist-put parts 'datetime datetime-part))
(setq parts (plist-put parts 'title file-title))
(setq parts (plist-put parts 'extension file-extension))
parts))))
(defun fmd/insert-org-header (header)
"Insert header"
(org-insert-heading t nil t)
(insert header))
(defun fmd/insert-org-subheader (subheader)
"Insert subheader"
(end-of-buffer)
(org-insert-heading t nil t)
(insert subheader)
(org-demote))
(defun fmd/insert-org-link (link title)
"Insert link"
(end-of-buffer)
(org-insert-heading t)
(org-insert-link nil link title))
(defun fmd/insert-document-option (key value)
"Insert document file option"
(insert (format "#+%s: %s\n" key value)))
(defun fmd/insert-file-headers (start-date)
"Insert default file headers"
(interactive)
(fm/insert-org-gpg-headers)
(end-of-visual-line)
(newline)
(fmd/insert-document-option "TITLE" (format "Media Diary of %s"
(format-time-string "%b. %Y" start-date)))
(fmd/insert-document-option "DATE" (format-time-string "%Y-%m" start-date))
(fmd/insert-document-option "STARTUP" "content")
(fmd/insert-document-option "OPTIONS" "toc:nil"))
(defun fmd/filter-files (directory prefix extension)
"Get the files based on prefix and extension"
(directory-files directory nil
(format-message "%s-.*\\.%s$" prefix extension)))
(defun fmd/once (f)
"Invoke a function only once, useful inside a loop"
(lexical-let ((g f)
(is-invoked nil))
(lambda (&rest args)
(if is-invoked nil
(progn
(setq is-invoked t)
(apply g args))))))
(defun fmd/compile-media-diary ()
"Compile the media diary, the diary must be compiled first with the shell script"
(interactive)
(lexical-let* ((md-prefix (s-trim (shell-command-to-string
(format "cat %s" fmd/md-compile-date-filter))))
(md-start-date
(lexical-let ((start-date-text (format "%s-01 00:00:00" md-prefix)))
(apply 'encode-time (parse-time-string start-date-text)))))
(lexical-let* ((render-header
(lambda ()
(fmd/insert-file-headers md-start-date)
(fmd/insert-org-header fmd/md-title)))
(render-note-section
(lambda ()
(fmd/insert-org-subheader fmd/md-note-title)
(newline-and-indent)
(insert fmd/md-summary-default-text)))
(render-journal-section
(lambda ()
(fmd/insert-org-subheader fmd/md-journal-title)
(lexical-let* ((journal-files (fmd/filter-files fmd/md-journal-dir md-prefix "org"))
(demote-first (fmd/once (lambda () (org-demote)))))
(mapc (lambda (journal-file-name)
(lexical-let* ((journal-file (expand-file-name journal-file-name fmd/md-journal-dir))
(journal-piece (fmd/parse-file-parts journal-file-name))
(journal-time (plist-get journal-piece 'datetime))
(journal-base-title (format-time-string fmd/md-datetime-format journal-time))
(journal-title
(lexical-let ((journal-buffer (find-file-noselect journal-file)))
(prog1
(fmd/get-title journal-buffer)
(kill-buffer journal-buffer))))
(journal-summary
(lexical-let ((journal-buffer (find-file-noselect journal-file)))
(prog1
(fmd/get-summary journal-buffer)
(kill-buffer journal-buffer))))
(journal-full-title
(format "%s - %s" journal-base-title journal-title)))
(fmd/insert-org-link (concat "file:" fmd/md-journal-dir-name "/" journal-file-name)
journal-full-title)
(funcall demote-first)
(newline-and-indent)
(insert (or journal-summary fmd/md-journal-summary-default-text))
(org-set-property "CREATED_ON"
(format-time-string "%F %T" journal-time))))
journal-files))))
(render-media-section
(lambda ()
(fmd/insert-org-subheader fmd/md-media-title)
(lexical-let* ((photo-files (fmd/filter-files fmd/md-photo-dir md-prefix fmd/md-photo-extension))
(audio-files (fmd/filter-files fmd/md-audio-dir md-prefix fmd/md-audio-extension))
(media-files
(sort (append photo-files audio-files)
(lambda (left right)
(lexical-let* ((result
(compare-strings
(file-name-sans-extension left) nil nil
(file-name-sans-extension right) nil nil
nil)))
(if (booleanp result) t
(if (> result 0) nil t))))))
(demote-first (fmd/once (lambda () (org-demote)))))
(intern-soft "photo")
(intern-soft "audio")
(mapc (lambda (media-file)
(lexical-let* ((media-piece (fmd/parse-file-parts media-file) )
(media-time (plist-get media-piece 'datetime))
(media-title (plist-get media-piece 'title))
(media-extension (plist-get media-piece 'extension))
(media-type (if (string-equal media-extension fmd/md-audio-extension)
'audio 'photo))
(media-time-format (if (eq media-type 'audio)
fmd/md-audio-datetime-format
fmd/md-photo-datetime-format))
(media-dir-name (if (eq media-type 'audio)
fmd/md-audio-dir-name fmd/md-photo-dir-name))
(media-base-title (format-time-string media-time-format media-time))
(media-full-title (if media-title (format "%s - %s" media-base-title media-title)
media-base-title)))
(fmd/insert-org-link (concat "file:" media-dir-name "/" media-file)
media-full-title)
(funcall demote-first)
(org-set-property "CREATED_ON"
(format-time-string "%F %T" media-time))))
media-files)))))
(find-file fmd/md-summary-file)
(funcall render-header)
(funcall render-note-section)
(funcall render-journal-section)
(funcall render-media-section)
(beginning-of-buffer)
(org-set-startup-visibility)
(save-buffer)
(fmd/build-media-diary-html))))
(defun fmd/build-media-diary-html ()
"Build the current diary as an html org project"
(interactive)
(org-publish
(list "fmd-html"
:base-directory fmd/md-root
:base-extension "org"
:publishing-directory fmd/md-root
:recursive t
:publishing-function 'org-html-publish-to-html
:html-extension "html"
:headline-levels 4
:section-numbers nil
:with-toc nil)
t))
Nice to see the standard buffers
(defun fn/open-message-buffer ()
(interactive)
(switch-to-buffer "*Messages*"))
This is just a convenience to open this config file
(defun fn/find-config-file ()
(interactive)
(find-file (expand-file-name "config.org" user-emacs-directory)))
Makes it easier to insert relative paths
(require 'f)
(defconst fn/relative-same-node "."
"Represents the same node on a path.")
(defconst fn/relative-parent-node ".."
"Represents the parent node on a path.")
(defun fn/relative-parent-node-thunk (&rest args)
"Always returns `fn/relative-parent-node'."
fn/relative-parent-node)
(defun fn/relative-path (source-path target-path)
"An attempt to compute the relative path from SOURCE-PATH to TARGET-PATH."
(letrec ((recurser
(lambda (upward-nodes downward-nodes parent-node)
(cond
((and (null upward-nodes) (null downward-nodes))
(string-join
(list fn/relative-same-node parent-node)
(f-path-separator)))
((null (car upward-nodes))
(string-join
(append (list fn/relative-same-node)
downward-nodes)
(f-path-separator)))
((null (car downward-nodes))
(string-join
(append (mapcar #'fn/relative-parent-node-thunk upward-nodes)
(list parent-node))
(f-path-separator)))
((string-equal (car upward-nodes)
(car downward-nodes))
(funcall recurser
(cdr upward-nodes)
(cdr downward-nodes)
(car upward-nodes)))
(t
(lexical-let* ((up-nodes (or (mapcar #'fn/relative-parent-node-thunk (cdr upward-nodes))
(list fn/relative-same-node)))
(down-nodes downward-nodes))
(string-join
(append up-nodes down-nodes)
(f-path-separator))))))))
(funcall recurser (f-split source-path) (f-split target-path) fn/relative-same-node)))
(defun fn/insert-relative-file-path (arg)
"Insert the relative file path of a source file to a target file.
If prefix ARG is present, select the source file."
(interactive "P")
(let* ((source-path
(cond
(arg
(car (find-file-read-args "Find source file: " t) ))
((buffer-file-name)
(buffer-file-name))
(t (error "Source file does not have a file name."))))
(target-path (car (find-file-read-args "Find target file: " t))))
(insert (fn/relative-path source-path target-path))))
A quick DSL makes me feel good.
(use-package magin
:load-path fn/custom-script-dir
:defer 1
:config
(magin-write-to-project
`(delimited
(context
(comment "Base files")
(file ,(format "%s.el" (file-name-base fn/config-file)))
(file ,(format "%s.el" (file-name-base fn/personal-file))))
(context
(comment "Base directory")
(dir ".cache")
(dir "extra"))
(context
(comment "Project specific")
(file "working-config.org")
(file ,(format "%s.el" (file-name-base fn/config-backup-file))))
(context
(comment "Library")
(path
"lib"
(context
(dir "sandbox")
(dir "packages"))
(include
(dir "modules")
(dir "scripts"))))
(block gtags)
(block emacs))
user-emacs-directory)
nil)
My blogging script
(defconst fn/org-jekyll-blogger-map (fn/make-prefixed-keymap (kbd "B") fn-standard-prefix-map)
"My custom org-jekyll-blogger map.")
(use-package org-jekyll-blogger
:load-path fn/custom-script-dir
:defer t
:bind (:map fn/org-jekyll-blogger-map
("d" . org-jekyll-blogger-create-draft)
("f" . org-jekyll-blogger-find-post)
("p" . org-jekyll-blogger-post-draft)))
I need some keybindings to select buffers quickly.
(defun fn/list-buffers-by (prompt pred)
"Like `list-buffers' but specialized by a predicate."
(completing-read
prompt
(mapcar
(lambda (buffer)
(cons (buffer-name buffer) buffer))
(buffer-list))
(lambda (pair)
(lexical-let* ((buffer (cdr pair)))
(funcall pred buffer)))))
(defconst fmb/list-buffer-map (fn/make-prefixed-keymap (kbd "B") fn-custom-prefix-map)
"My list buffer map.")
(defconst fmb/command-prefix "fmb/"
"My command key prefix.")
(defun fn/define-custom-switch-buffer (command-name key prompt pred)
"Create a quick list buffer command."
(lexical-let* ((full-command-name
(format "%s%s" fmb/command-prefix command-name))
(command-symbol
(intern full-command-name))
(pred pred)
(command
`(lambda ()
,(format "List buffers with %s" command-name)
(interactive)
(switch-to-buffer
(fn/list-buffers-by ,prompt ,pred)))))
(fset command-symbol command)
(define-key fmb/list-buffer-map key command-symbol)))
(defun fn/define-custom-switch-buffer-by-mode (key prompt main-mode)
"Create a quick list buffer command by mode."
(fn/define-custom-switch-buffer
(symbol-name main-mode)
key prompt
(lexical-let ((main-mode main-mode))
(lambda (buffer) (eq (with-current-buffer buffer major-mode) main-mode)))))
(defun fn/define-custom-switch-buffer-by-minor-mode (key prompt sub-mode)
"Create a quick list buffer command by minor mode."
(fn/define-custom-switch-buffer
(symbol-name sub-mode)
key prompt
(lexical-let ((sub-mode sub-mode))
(lambda (buffer)
(with-current-buffer buffer
(and (boundp sub-mode) (symbol-value sub-mode)))))))
;; Major Modes
(fn/define-custom-switch-buffer-by-mode "w" "Select a page: " 'w3m-mode)
(fn/define-custom-switch-buffer-by-mode "e" "Select an (e)shell: " 'eshell-mode)
(fn/define-custom-switch-buffer-by-mode "i" "Select an (e)rc channel: " 'erc-mode)
;; Minor Modes
(fn/define-custom-switch-buffer-by-minor-mode "o" "Select a org-source buffer: " 'org-src-mode)
Some contrivances
(fn/use-executables espeak-check
(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)))))
A quick definition for diction checking.
(fn/use-executables diction-check
(diction)
(fn/use-feature flycheck-diction
(flycheck)
(defconst fn/diction-dir (expand-file-name "diction" fn/system-dir)
"My diction directory.")
(make-directory fn/diction-dir t)
(defconst fn/flycheck-diction-file (expand-file-name "en-conf" fn/diction-dir)
"Diction conf file.")
(defun fn/flycheck-diction--error-line-parser (raw-line)
"Parse a line into relevant parts."
(lexical-let ((file
(prog2
(string-match "\\(.*\\):\\([0123456789]+\\):\\(.*\\)" raw-line)
(match-string-no-properties 1 raw-line)))
(line
(prog2
(string-match "\\(.*\\):\\([0123456789]+\\):\\(.*\\)" raw-line)
(match-string-no-properties 2 raw-line)))
(errors
(lexical-let ((processed-line
(replace-regexp-in-string "\\[\\[.+\\]\\[.+\\]\\]" "" raw-line))
(index 0)
(matches (list)))
(while (not (null index))
(setq index (string-match "\\\[\\(.+?\\)\s->\s\\(.+?\\)\\\]" processed-line
(1+ index)))
(push
(cons (match-string-no-properties 1 processed-line)
(match-string-no-properties 2 processed-line))
matches))
matches)))
(list file (string-to-number line) errors)))
(defun fn/flycheck-diction--error-parser (output checker buffer)
"Parser for diction"
(lexical-let ((result
(cl-reduce
(lambda (acc raw-line)
(if (string-empty-p raw-line)
acc
(pcase-let ((`(,file ,line ,errors)
(fn/flycheck-diction--error-line-parser
(string-trim raw-line))))
(append
acc
(mapcar
(lambda (this-error)
(flycheck-error-new
:buffer buffer
:checker checker
:filename file
:line line
:message (concat (car this-error) " -> " (cdr this-error))
:level 'warning))
errors)))))
(split-string (string-trim output) "\n")
:initial-value (list)
:end -1 ;; Exclude ending line
)))
result))
(flycheck-define-command-checker 'fn/diction-checker
"Checking with diction."
:command `("diction" "-f" ,fn/flycheck-diction-file "-s" "-b" "-d" source)
:error-parser 'fn/flycheck-diction--error-parser
:standard-input nil
:modes 'org-mode)
(add-to-list 'flycheck-checkers 'fn/diction-checker)))
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))))
Just utilities to remove async buffers.
(progn
(require 'cl)
(defun fn/kill-inactive-async-buffers ()
(interactive)
(->> (buffer-list)
(--filter
(and
(string-prefix-p
"*Async Shell Command*"
(buffer-name it))
(not (get-buffer-process it))))
(--map (kill-buffer it))
(length)
(message "Killed %s inactive async buffers"))))
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"
(lexical-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)
;; System Buffers
(define-key fn-custom-prefix-map (kbd "b m") 'fn/open-message-buffer)
;; 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 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 ^") #'fn/dired-parent)
(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)
(define-key fn-custom-prefix-map (kbd "i p") #'fn/insert-relative-file-path)
;; 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)