codemac emacs
Emacs outshines all other editing software in approximately the same way that the noonday sun does the stars. It is not just bigger and brighter; it simply makes everything else vanish.
– Neal Stephenson, “In the Beginning was the Command Line”
Introduction
The goal of this init.org file is to document my emacs configuration in a readable manner for those who would like to borrow from it. We’ll see how this goes!Org initialization
Unfortuantely, emacs can’t read directly from org mode files just yet, so you have to check init.el for more information! DRY!Lexical lisp
This must be first, and it makes everything else a little bit easier.
;;; -*- lexical-binding: t -*-Security
There was a very elucidating and concerning blog post that showed how tls is configured in emacs. This is some code to turn on tls verification and try to do our best. This can probably be worked around by an APT, but this is best effort for now.
First we find the host’s ssl certs. For every host this should be configured manually, as detection can lead to other trust bugs.
(setq
cm/machine-trust-file
(cond
((string-prefix-p "nevada" system-name) (concat (getenv "HOME") "/.guix-profile/etc/ssl/certs/ca-certificates.crt"))
((string-prefix-p "nordic" system-name) "/etc/ssl/certs/ca-certificates.crt")
((string-prefix-p "novaria" system-name) "/etc/ssl/certs/ca-certificates.crt")
((string-prefix-p "neah" system-name) "/etc/ssl/certs/ca-certificates.crt")
((string-prefix-p "jmickey-glaptop" system-name) "/etc/ssl/certs/ca-certificates.crt")
((string-prefix-p "manatee" system-name) "/etc/ssl/certs/ca-certificates.crt")
(t (error "No tls trust set for this host! Add exception or find cert.pem you trust"))))The default value for tls-program this is:
("gnutls-cli --insecure -p %p %h" "gnutls-cli --insecure -p %p %h --protocols ssl3" "openssl s_client -connect %h:%p -no_ssl2 -ign_eof")
But notice all that --insecure.. this is what we get rid of!
Now that we have a cm/machine-trust-file we set up our tls-program
correctly.
(setq tls-program
(concat "gnutls-cli --x509cafile " cm/machine-trust-file " -p %p %h"))If emacs is compiled to link against gnutls, it usually totally different variables and set up, because go fuck yourself users, you gotta configure tls super manually.
(setq gnutls-verify-error t)
(setq gnutls-trustfiles (list cm/machine-trust-file))Utility Functions
async-shell-command-to-string
(defun async-shell-command-to-string (command callback)
"Execute shell command COMMAND asynchronously in the
background.
Return the temporary output buffer which command is writing to
during execution.
When the command is finished, call CALLBACK with the resulting
output as a string."
(lexical-let ((output-buffer (generate-new-buffer " *temp*"))
(callback-fun callback))
(set-process-sentinel
(start-process "Shell" output-buffer shell-file-name shell-command-switch command)
(lambda (process signal)
(when (memq (process-status process) '(exit signal))
(with-current-buffer output-buffer
(let ((output-string
(buffer-substring-no-properties (point-min) (point-max))))
(funcall callback-fun output-string)))
(kill-buffer output-buffer))))
output-buffer))User Interface
Emacs look
(if (boundp 'menu-bar-mode)
(menu-bar-mode -1))
(if (boundp 'tool-bar-mode)
(tool-bar-mode -1))
(if (boundp 'scroll-bar-mode)
(scroll-bar-mode -1))I love the empty-lines notification. However, it does seem to fail on the very last line? I haven’t solved this yet.
(set-default 'indicate-empty-lines t)Display all the warnings! I’ve had this forever, I’m not so sure how important it actually is.
(setq warning-suppress-types nil)Also, I hate the audible bell, lots.
(setq visible-bell 1)This adds line numbers and column numbers in the emacs modeline. Fucking essential.
(line-number-mode 1)
(column-number-mode 1)Let’s get weird.
(setq enable-recursive-minibuffers t)And of course, transparency!
;; Set transparency of emacs
(defun transparency (value)
"Sets the transparency of the frame window. 0=transparent/100=opaque"
(interactive "nTransparency Value 0 - 100 opaque:")
(set-frame-parameter (selected-frame) 'alpha value))
(defun netflix ()
(interactive)
(set-background-color "black")
(transparency 45))
(defun opaque ()
(interactive)
(load-theme 'zenburn t)
(set-cursor-color "yellow")
(setq cursor-type 'box)
(transparency 100))Modeline management
(defun cm/display-time-mail-function ()
"Return t if new important mail, else nil"
(let* ((s (shell-command-to-string
"notmuch count tag:inbox AND tag:unread"))
(st (string-trim s))
(newmail (string= "0" st)))
(not newmail)))
(setq display-time-mail-function nil) ;; not actually useful, always have mail
(setq display-time-format "%Y-%m-%d %H:%M")
(setq display-time-day-and-date t)
(setq display-time-24hr-format t)
(setq display-time-use-mail-icon t)
(setq display-time-default-load-average nil)
(display-time-mode t)Emacs Title Bar
RescueTime and other resources use the title bar to assess what an application is doing. I use that to include the mode of the file I’m currently looking at, so I can categorize what I’m doing in emacs. Unfortunately, I haven’t figured out how to include the projectile project as well, as this would make it easy to see what was “in scope” for something.
(setq frame-title-format '("%m " invocation-name "@" system-name))System sepecific settings
Had to do some funky stuff to get around how shitty Mac OS X default VPN settings are. Also, set different font sizes for the different screen DPI’s.(defun cm/produce-font (size)
(let ((font (concat "DejaVu Sans Mono-"
(if (numberp size) (number-to-string size) size)
":hinting=true:autohint=true")))
(add-to-list 'initial-frame-alist `(font . ,font))
(add-to-list 'default-frame-alist `(font . ,font))))
(cond
((or (string-prefix-p "phoenix-mta" system-name)
(string-prefix-p "vpn2ntap-" system-name)
(string-prefix-p "novaria" system-name)
(string-prefix-p "moc.ppaten" (apply 'string (reverse (string-to-list system-name)))))
(cm/produce-font 12))
((equal system-name "penolpe")
(cm/produce-font 9))
(t
(cm/produce-font 11)))
;; gotta figure out a better way for unicode
(cond
((member "Noto Emoji" (font-family-list))
(set-fontset-font t 'unicode "Noto Emoji" nil 'prepend))
((member "symbola" (font-family-list))
(set-fontset-font t 'unicode "Symbola" nil 'prepend)))
Environment
Common Lisp
Common lisp has things likecl-loop, which are life.
(require 'cl)Yes I can scroll left
(put 'scroll-left 'disabled nil)Dvorak
The dvorak keyboard layout is really advantageous to those of us who write emails all day (read: me). However, it does create some problem with things likeC-x in Emacs. In dvorak, x is b on the
keyboard, meaning you’re reaching quite far with your hand.
I’m going to try out switching C-t and C-x as per suggested by Xah
Lee’s page on the dvorak C-x problem.
(keyboard-translate ?\C-x ?\C-t)
(keyboard-translate ?\C-t ?\C-x)Also, let’s get angry about using C-x until I’m used to it actually being C-t.
(global-set-key
(kbd "C-t")
(lambda ()
(interactive)
(run-with-timer
0.3 nil
(lambda ()
;; Assuming these are the default values
(setq visible-bell nil)
(setq ring-bell-function 'ignore)))
(setq visible-bell t)
(setq ring-bell-function nil)
(error "Don't press that button.")))Shell paths
Add some normal locations to the path in case it’s not when I click an emacs icon (instead of launching from the shell).
(when (eq system-type 'gnu/linux)
(let* ((home-dir (getenv "HOME"))
(linux-paths
`(,(concat home-dir "/bin")
,(concat home-dir "/.guix-profile/bin"))))
(setenv "PATH" (concat (mapconcat 'identity linux-paths ":")
":"
(getenv "PATH")))
(setq exec-path (append linux-paths exec-path))))Setting paths correctly depending on whether or not I’m on a Mac. Mostly these days, I’m on a Mac :/
(when (eq system-type 'darwin)
(prefer-coding-system 'utf-8)
(setq file-precious-flag t)
(let* ((home-dir (getenv "HOME"))
(mac-paths `("/Applications/Emacs.app/Contents/MacOS/bin"
,(concat home-dir "/bin")
,(concat home-dir "/.cabal/bin")
"/opt/local/bin"
"/usr/local/texlive/2010/bin/x86_64-darwin"
"/usr/local/bin"
"/usr/local/sbin"
"/usr/bin"
"/usr/sbin"
"/bin"
"/sbin")))
(setenv "PATH" (concat (mapconcat 'identity mac-paths ":")
":"
(getenv "PATH")))
(setq exec-path (append exec-path mac-paths))))Emacs load paths
Get my site-lisp set up. Got rid of ye old pkg-init!(add-to-list 'load-path "~/.emacs.d/site-lisp")Also, load sub directories underneath site-lisp. This lets me copy random tar balls of elisp without having to worry how it’s all formatted.
Special thanks to benny, who started me down this epic emacs journey who provided this original functionality for me.
(defun dirs-inside-directory (parent)
(let (foo)
(dolist (file (directory-files parent t))
(when (and (not (member (file-name-nondirectory file)
'("." "..")))
(file-directory-p file))
(setq foo (cons file foo))))
foo))
;; Automagically load all folders in site-lisp as well! Thank you benny!
(setq cm/lisp-dirs '("~/.emacs.d/site-lisp"
"~/.emacs.d/site-lisp/xelb"
"~/.guix-profile/share/emacs/site-lisp"
"~/.guix-profile/share/emacs/site-lisp/guix.d"))
(mapc (lambda (x) (when (file-directory-p x)
(mapc (lambda (y)
(add-to-list 'load-path y))
(dirs-inside-directory x))))
cm/lisp-dirs)Guix Support
This adds the load path of the guix profile of the current user. I’m currently using guix to manage packages for my local users.This probably works better in GuixSD - but the systemd / dmd crap and the icecat / firefox crap keeps me using arch as my base system.
(add-to-list 'load-path "~/.guix-profile/share/emacs/site-lisp")Emacs backup files
I like putting these all in one place. It helps to not have them scattered accross my entire filesystem, and then they aren’t pushed out onto NFS mounted directories.There are drawbacks. If you edit the same file over an NFS mount from different emacs instances over time, they wont have eachother’s autosaves. I have never run into this being a problem, however.
A lot of this was borrowed from http://snarfed.org/space/gnu%20emacs%20backup%20files, however it appears this as changed significantly..
(defvar autosave-dir
(concat "/tmp/emacs_autosaves/" (user-login-name) "/"))
(make-directory autosave-dir t)
(defun auto-save-file-name-p (filename)
(string-match "^#.*#$" (file-name-nondirectory filename)))
(defun make-auto-save-file-name ()
(concat autosave-dir
(if buffer-file-name
(concat "#" (file-name-nondirectory buffer-file-name) "#")
(expand-file-name
(concat "#%" (buffer-name) "#")))))
;; Put backup files (ie foo~) in one place too. (The backup-directory-alist
;; list contains regexp=>directory mappings; filenames matching a regexp are
;; backed up in the corresponding directory. Emacs will mkdir it if necessary.)
(defvar backup-dir (concat "/tmp/emacs_backups/" (user-login-name) "/"))
(setq backup-directory-alist (list (cons "." backup-dir)))Yes or no, let’s do y/p
Oh my freaking god, just take my damn answer.(fset 'yes-or-no-p 'y-or-n-p)Async Shell Command
This makes sure that we pick a new buffer and just run with it, instead of checking if another process is running.(setq async-shell-command-buffer 'rename-buffer)Convenient global keys
God I love backword-kill-word. Also some bookmarks(global-set-key "\C-w" 'backward-kill-word)
(global-set-key "\C-x\C-k" 'kill-region)
;; not really using this..
;(global-set-key [f5] 'bookmark-bmenu-list)
;(global-set-key [f6] 'bookmark-set)
;(global-set-key [f7] 'bookmark-jump)
(defun cm/backward-kill (killwordf &optional arg)
"Replacement for the backward-kill-word command
If the region is active, then invoke kill-region. Otherwise, use
the following custom backward-kill-word procedure.
If the previous word is on the same line, then kill the previous
word. Otherwise, if the previous word is on a prior line, then kill
to the beginning of the line. If point is already at the beginning
of the line, then kill to the end of the previous line.
With argument ARG and region inactive, do this that many times."
(interactive "p")
(if (use-region-p)
(kill-region (mark) (point))
(let (count)
(dotimes (count arg)
(if (bolp)
(delete-backward-char 1)
(kill-region (max (save-excursion
(funcall killwordf arg)
(point))
(line-beginning-position))
(point)))))))
;; handle subword / superword modes as well!
(defun cm/backward-kill-subword (&optional arg)
(interactive "p")
(cm/backward-kill 'subword-backward-kill arg))
; I don't think this is necessary, but we'll see
;(defun cm/backward-kill-superword (&optional arg)
; (interactive "p")
; (cm/backward-kill 'superword-backward-kill arg))
(defun cm/backward-kill-word (&optional arg)
(interactive "p")
(cm/backward-kill 'backward-kill-word arg))
(define-key (current-global-map)
[remap backward-kill-word] 'cm/backward-kill-word)
;; get subword's map
(require 'subword)
(define-key subword-mode-map
[remap backward-kill-word] 'cm/backward-kill-subword)Narrow regions
(put 'narrow-to-region 'disabled nil)kill with linum
Really useful for source code copying.. This is from stack overflow.(defun kill-with-linenum (beg end)
(interactive "r")
(save-excursion
(goto-char end)
(skip-chars-backward "\n \t")
(setq end (point))
(let* ((chunk (buffer-substring beg end))
(chunk (concat
(format "╭──────── #%-d ─ %s ──\n│ "
(line-number-at-pos beg)
(or (buffer-file-name) (buffer-name)))
(replace-regexp-in-string "\n" "\n│ " chunk)
(format "\n╰──────── #%-d ─"
(line-number-at-pos end)))))
(kill-new chunk)))
(deactivate-mark))goto-line should work on first M-g
taken from http://blog.akinori.org/2013/05/27/m-g-vs-goto-line/(defun cm/goto-line-number ()
(interactive)
(goto-line (string-to-number
(read-from-minibuffer
"Goto line: "
(char-to-string last-command-event)))))
(cl-loop for n from 1 to 9 do
(global-set-key (format "\M-g%d" n) 'cm/goto-line-number))
(global-set-key "\M-g?" 'describe-prefix-bindings)Use dired instead of the directory view.
I never expect it, and when I get it it means I meant dired anyways. This means that how I hold down the control button doesn’t matter.(global-set-key (kbd "C-x C-d") 'dired)Toggle Layout
(defun toggle-window-split ()
(interactive)
(if (= (count-windows) 2)
(let* ((this-win-buffer (window-buffer))
(next-win-buffer (window-buffer (next-window)))
(this-win-edges (window-edges (selected-window)))
(next-win-edges (window-edges (next-window)))
(this-win-2nd (not (and (<= (car this-win-edges)
(car next-win-edges))
(<= (cadr this-win-edges)
(cadr next-win-edges)))))
(splitter
(if (= (car this-win-edges)
(car (window-edges (next-window))))
'split-window-horizontally
'split-window-vertically)))
(delete-other-windows)
(let ((first-win (selected-window)))
(funcall splitter)
(if this-win-2nd (other-window 1))
(set-window-buffer (selected-window) this-win-buffer)
(set-window-buffer (next-window) next-win-buffer)
(select-window first-win)
(if this-win-2nd (other-window 1))))))Use rc escaping
I’ve now set my login shell as rc, which means that now all kinds of arbitrary things break.Luckily, the shell-quote-argument function is really easy to write,
because it really just is string replacement on the single quote. And
unquoting isn’t even a thing because rc doesn’t fucking suck at life,
it just passes on arguments. It’s wonderful!
(defvar cm/using-rc t
"Set to true when I'm using rc as my main shell")
(defun cm/advise-shell-quote-argument (fun &rest args)
(if (and cm/using-rc
(not (file-remote-p default-directory)))
(concat "'" (replace-regexp-in-string "'" "''" (if (listp args) (car args) args) t t) "'")
(apply fun args)))
(advice-add #'shell-quote-argument :around #'cm/advise-shell-quote-argument)
Support for rgrep as well
This changes thegrep-find-template so that quoting is used around
the brackets that rc parses.
(eval-after-load "grep"
(lambda ()
(grep-apply-setting 'grep-find-template "find . <X> -type f <F> -exec grep <C> -nH -e <R> '{}' '+'")))Performance improvement of vertical window scroll
https://emacs.stackexchange.com/questions/28736/emacs-pointcursor-movement-lag/28746
This just removes some of the window scroll calculus, which improves
C-n speed. This should really not be something I have to set
however.
(setq auto-window-vscroll nil)defun-local
This uses two macros, that make it easier for me to define commands
(like org-agenda, notmuch-search, etc) that shouldn’t regularly
depend on TRAMP. If they do, make sure it’s not because of the current
buffer.
This may mess up org-agenda if I’m doing crazier agenda stuff later.
(defmacro cm/defun-local (funcname args &rest body)
"Create a function that always operates without tramp on the local system."
`(defun ,funcname ,args
(interactive)
(with-current-buffer (get-buffer-create "*scratch*")
,@body)))
(defmacro cm/defun-local-wrap (funcname towrap)
"Create a function that always operates without tramp on the local system."
`(cm/defun-local ,funcname () (,towrap)))
ELPA
I organize my packages use with the amazing https://github.com/jwiegley/use-package(eval-when-compile
(require 'use-package))
(require 'diminish)
(require 'bind-key)Color theme
Yup, zenburn.If zenburn isn’t available, we should use wombat. So how do we detect that?
;; (if (custom-theme-name-valid-p 'zenburn)
;; (load-theme 'zenburn t)
;; (load-theme 'wombat t))
(load-theme 'wombat t)For now we just check that the name is valid.
Builtin
Disabled Functions
(put 'downcase-region 'disabled nil)
(put 'upcase-region 'disabled nil)Open files with root
(defun cm/rename-tramp-buffer ()
(when (file-remote-p (buffer-file-name))
(rename-buffer
(format "%s:%s"
(file-remote-p (buffer-file-name) 'method)
(buffer-name)))))
;;(add-hook 'find-file-hook
;; 'cm/rename-tramp-buffer)
(defadvice find-file (around th-find-file activate)
"Open FILENAME using tramp's sudo method if it's read-only."
(let ((thefile (ad-get-arg 0)))
(if (or (string-prefix-p "/etc" thefile)
(string-prefix-p "/boot" thefile))
(if (and (not (file-writable-p thefile))
(y-or-n-p (concat "File "
thefile
" is read-only. Open it as root? ")))
(cm/find-file-sudo thefile))))
ad-do-it)
(defun cm/find-file-sudo (file)
"Opens FILE with root privileges."
(interactive "F")
(set-buffer (find-file (concat "/sudo::" file))))Doc View
Increase the DPI to a factor of most screen DPI’s.
(setq doc-view-resolution 288)Occur, isearch, and all
I like to launch occur from an isearch query. It’s great when your muscle memory searches for a word, THEN you realize you want to see it all in one buffer. Rock theC-c C-e in occur mode and you can edit
everything! heck yes!
This was mostly taken from the emacs wiki.
(defun isearch-occur ()
"Invoke `occur' from within isearch."
(interactive)
(let ((case-fold-search isearch-case-fold-search))
(occur (if isearch-regexp isearch-string (regexp-quote isearch-string)))))
(define-key isearch-mode-map (kbd "C-o") 'isearch-occur)Ediff
Split the Ediff window depending on the orientation/size of the emacs frame. I’ve found this very convenient.(setq ediff-split-window-function (lambda (&optional arg)
(if (> (frame-width) 150)
(split-window-horizontally arg)
(split-window-vertically arg))))Useful window functions
from : http://www.emacswiki.org/emacs/Rick_Bielawski;; Idea and starter code from Benjamin Rutt (rutt.4+news@osu.edu) on comp.emacs
(defun window-horizontal-to-vertical ()
"Switches from a horizontal split to a vertical split."
(interactive)
(let ((one-buf (window-buffer (selected-window)))
(buf-point (point)))
(other-window 1)
(delete-other-windows)
(split-window-horizontally)
(switch-to-buffer one-buf)
(goto-char buf-point)))
;; complement of above created by rgb 11/2004
(defun window-vertical-to-horizontal ()
"Switches from a vertical split to a horizontal split."
(interactive)
(let ((one-buf (window-buffer (selected-window)))
(buf-point (point)))
(other-window 1)
(delete-other-windows)
(split-window-vertically)
(switch-to-buffer one-buf)
(goto-char buf-point)))Private stuff
Just an easy way to put passwords, and other sensitive data outside of this emacs config! Ideally I’ll document all variables missing, this may or may not be always true though.;; irc
(defvar cm/freenode-password "nope" "The nickserv password for freenode.")
(defvar cm/oftc-password "nope" "The nickserv password for oftc.")
(defvar cm/what-password "nope" "The nickserv password for what.")
(defvar cm/rizon-password "nope" "The nickserv password for rizon.")
(defvar cm/bitlbee-password "nope" "The password for bitlbee!")
(defvar cm/slack-password "nope" "The password for PureStorage IRC integration")
(defvar cm/rcirc-channel-alist '(("freenode" "#archlinux" "#emacs")
("oftc" "#ikiwiki"))
"The channel list..")
(defvar cm/identica-username "nope" "The password for bitlbee!")
(defvar cm/identica-password "nope" "The password for bitlbee!")
(defvar cm/erc-keywords "nope" "The password for bitlbee!")
(defvar cm/erc-autojoin-channels-alist '("#emacs") "the default list for erc chan.")
(defvar cm/rcirc-server-alist '() "The password for bitlbee!")
(defvar cm/ironport-p4port "" "p4port")
(defvar cm/mu4e-refile-folder (lambda (x) '()) "refile!")
(defvar cm/erc-track-exclude "" "track exclude")
(let ((private-file "~/.emacs-priv.el"))
(when (file-exists-p private-file)
(load-file private-file)))Printing
;(require 'lpr)
(setq lpr-command "gtklp")Sticky buffer
Inspired by a reddit comment.;; (defadvice pop-to-buffer (before cancel-other-window first)
;; (ad-set-arg 1 nil))
;; (ad-activate 'pop-to-buffer)
;; Toggle window dedication
(defun toggle-window-dedicated ()
"Toggle whether the current active window is dedicated or not"
(interactive)
(message
(if (let (window (get-buffer-window (current-buffer)))
(set-window-dedicated-p window
(not (window-dedicated-p window))))
"Window '%s' is dedicated"
"Window '%s' is normal")
(current-buffer)))
;; Press [pause] key in each window you want to "freeze"
(global-set-key [f11] 'toggle-window-dedicated)Narrow to indirect buffer
There was a blog post on reddit about this, and It’s too good to not use. I haven’t decided what the key binding should really be yet. Al(defun cm/narrow-to-region-indirect (start end)
"Restrict editing in this buffer to the current region, indirectly."
(interactive "r")
(when (fboundp 'evil-exit-visual-state) ; There's probably a nicer way to do this
(evil-exit-visual-state))
(let ((buf (clone-indirect-buffer nil nil)))
(with-current-buffer buf
(narrow-to-region start end))
(switch-to-buffer buf)))
(global-set-key (kbd "C-x n i") 'cm/narrow-to-region-indirect)Revert
To revert a buffer easily, put the char back where I had it.(defun cm/revert-buffer ()
"save the current position to tmp, revert buffer, go back to tmp"
(interactive)
(cm/revert-specific-buffer (current-buffer)))
(defun cm/revert-specific-buffer (buf)
"save the current position to tmp, revert buffer, go back to tmp"
(interactive "bBuffer: ")
(with-current-buffer buf
(let ((tmp (point)))
(revert-buffer t)
(goto-char tmp))))
(global-set-key [f8] 'cm/revert-buffer)Windmove
Easy navigation around lots of splits. C-x o isn’t that geographical.(global-set-key (kbd "s-<left>") 'windmove-left)
(global-set-key (kbd "s-<right>") 'windmove-right)
(global-set-key (kbd "s-<up>") 'windmove-up)
(global-set-key (kbd "s-<down>") 'windmove-down)Dired
This is what is sent to ls. I’m usually on a *nix-like userspace, so ls usually exists. On windows emacs uses some ls elisp, I’m not sure if these settings work for that.(setq dired-listing-switches "-ahlF")On Mac OS X, ls -F prints an @ symbol when printing symlinks. This setting lets dired know that this is the case.
(when (eq system-type 'darwin)
(setq dired-ls-F-marks-symlinks t))Disabled: set dired to reuse buffers
Every time you hit enter, instead of opening a new buffer, it’ll reuse the buffer. I’ve found I don’t like using this, but it was very useful when I first started using emacs, less so later.Have ^ and Enter open the next directory in the same buffer. I
don’t think there is a situation where I don’t want this to happen, so
we’ll roll with this.
;; reenable!
(put 'dired-find-alternate-file 'disabled nil)
;; (add-hook 'dired-mode-hook
;; (lambda ()
;; (define-key dired-mode-map (kbd "<return>")
;; 'dired-find-alternate-file) ; was dired-advertised-find-file
;; (define-key dired-mode-map (kbd "a")
;; 'dired-advertised-find-file) ; was dired-find-alternate-file
;; ; was dired-up-directory
;; (define-key dired-mode-map (kbd "^")
;; (lambda () (interactive) (find-alternate-file "..")))))Insert subdirectory while folding the parent directory
Useful when navigating down through a hierachy that you may care about later, but aren’t sure.
(defun cm/insert-n-hide-dired ()
(interactive)
(let ((fn (dired-get-filename)))
(dired-hide-subdir 1)
(dired-maybe-insert-subdir fn)))
(require 'dired)
(define-key dired-mode-map "I" 'cm/insert-n-hide-dired)Info
Mac fix for info installation location!(require 'info)
(when (eq system-type 'darwin)
(setq Info-directory-list
(cons
(expand-file-name "/opt/local/share/info/")
Info-directory-list)))Pcomplete
(require 'pcmpl-git)Shell (using rakitzis’ rc)
Shell Session Management
I need to tie this into projectile, but for now have a way to create a “main” buffer and then name other ones withshell-new.
;; give shell advice to load dir-locals
(defun cm/advise-shell (&rest r)
(hack-dir-local-variables-non-file-buffer))
(advice-add #'shell :before #'cm/advise-shell)
(defun cm/shell-new (name)
"Create a shell buffer named NAME."
(interactive "sShell Name: ")
(let* ((bn (concat "*shell:" name "*"))
(eb (get-buffer bn)))
(if (and eb (get-buffer-process eb))
(switch-to-buffer eb)
(shell bn))))
(defun cm/current-shells ()
(require 'subr-x)
(delq nil
(mapcar
(lambda (x)
(if (string-prefix-p "*shell:" (buffer-name x))
`(,(string-remove-prefix "*shell:" (string-remove-suffix "*" (buffer-name x))) ,x)))
(buffer-list))))
(defun cm/shell-find-or-new ()
"Find or create a shell with the given name"
(interactive)
(let ((selected-shell (completing-read
"Shell Name: "
(cm/current-shells))))
(cm/shell-new selected-shell)))
(defun cm/shell-main ()
(interactive)
(cm/shell-new "main"))
(defun cm/shell-projectile ()
(interactive)
(projectile-with-default-dir (projectile-project-root)
(cm/shell-new (projectile-project-name))))
(global-set-key (kbd "<f2>") 'cm/shell-find-or-new)
(global-set-key (kbd "<f7>") 'cm/shell-main)
(global-set-key (kbd "C-c p $") 'cm/shell-projectile)Remote Shells
This is to manage remote shells. I hope for this to get a lot more sophisticated, as ideally you’d be able to restore scrollback as well. Ideally dtach would actually have support for replaying scrollback, but not sure how it would handle it given it doesn’t really interpret anything.
(defun cm/ssh-dtach-list-sessions (host)
(map 'list (lambda (x) (string-remove-prefix ".dtach.emacs." x))
(directory-files (concat "/ssh:" host ":/tmp/") nil "^\\.dtach\\.emacs\\.." t)))
(defun cm/ssh-dtach (host session)
"Open SSH connection to HOST with SESSION and start dtach session."
(interactive
(let* ((host (completing-read "Host: " cm/machines nil 'confirm))
(session (completing-read "Session: " (cm/ssh-dtach-list-sessions host) nil 'confirm)))
(list host session)))
(let ((explicit-shell-file-name "dtach")
(explicit-dtach-args `("-A" ,(concat "/tmp/.dtach.emacs." session) "-z"
"/usr/bin/rc" "-l"))
(default-directory (concat "/ssh:" host ":/")))
(shell (format "*shell:%s-%s*" host session))))Use a login shell
(set-default 'explicit-shell-file-name (concat (getenv "HOME") "/bin/rc"))
(setq explicit-rc-args '("-l"))
(when (string-equal (system-name) "jmickey-glaptop0")
(set-default 'explicit-shell-file-name (concat (getenv "HOME") "/bin/rc"))
(setq explicit-rc-args nil))Track the directory of the shell process
(defun shell-procfs-dirtrack (str)
(prog1 str
(if (stringp str)
(let ((directory (file-symlink-p
(format "/proc/%s/cwd"
(process-id
(get-buffer-process
(current-buffer)))))))
(if directory
(when (file-directory-p directory)
(cd directory)))))))
(define-minor-mode shell-procfs-dirtrack-mode
"Track shell directory by inspecting procfs."
nil nil nil
(cond (shell-procfs-dirtrack-mode
(when (bound-and-true-p shell-dirtrack-mode)
(shell-dirtrack-mode 0))
(when (bound-and-true-p dirtrack-mode)
(dirtrack-mode 0))
(add-hook 'comint-preoutput-filter-functions
'shell-procfs-dirtrack nil t))
(t
(remove-hook 'comint-preoutput-filter-functions
'shell-procfs-dirtrack t))))(require 'tramp)
(setq comint-scroll-to-bottom-on-input t ; always insert at the bottom
comint-scroll-to-bottom-on-output nil ; always add output at the bottom
comint-scroll-show-maximum-output t ; scroll to show max possible output
comint-input-ignoredups t ; no duplicates in command history
comint-completion-addsuffix t ; insert space/slash after file completion
comint-buffer-maximum-size 40000 ; max length of the buffer in lines
comint-prompt-read-only t ; if this is t, it breaks shell-command (we'll see about that)
comint-get-old-input (lambda () "") ; what to run when i press enter on a
; line above the current prompt
comint-input-ring-size 5000 ; max shell history size
protect-buffer-bury-p nil)
;; make sure that comint in shell mode doesn't try to quote file
;; names! this is annoying as fuck!
(setq shell-file-name-quote-list nil)
(setenv "PAGER" "cat")
(setenv "MANPAGER" "cat")
;; truncate buffers continuously
(add-hook 'comint-output-filter-functions 'comint-truncate-buffer)
;; interpret and use ansi color codes in shell output windows is the
;; default. If you want to filter out there existence, then do the
;; following:
;; (add-hook 'shell-mode-hook 'ansi-color-for-comint-mode-filter)
;;
;; instead, I'm going to have it do nothing!
(add-hook 'shell-mode-hook 'ansi-color-for-comint-mode-off)
(defun set-scroll-conservatively ()
"Add to shell-mode-hook to prevent jump-scrolling on newlines in shell buffers."
(set (make-local-variable 'scroll-conservatively) 10))
(add-hook 'shell-mode-hook 'set-scroll-conservatively t)
(add-hook 'shell-mode-hook 'shell-procfs-dirtrack-mode t)
(add-hook 'shell-mode-hook (lambda () (font-lock-mode -1)))
Use a better async-shell-command
The *Async Shell Command* buffer naming is bullshit. Here is
something much better, it names each buffer *shell:<cmd>* so my
above shell searching functions also find these buffers. Eventually
I’d like something where these are launched automatically from any
command run in shell mode, but we’ll see.
(defun cm/exec-shell (oldfun scmd)
(interactive "sShell Command: ")
(let* ((shell-to-exec (or (locate-file "rc" exec-path) "/bin/sh"))
(base-name (replace-regexp-in-string "[^a-z]+" "-" (downcase scmd)))
(short-name (if (> (length base-name) 15) (substring base-name 0 15) base-name))
(unique-name (concat "*shell:" short-name "*"))
(name-idx 0))
(while (get-buffer unique-name)
(setq name-idx (+ name-idx 1))
(setq unique-name (concat "*shell:" short-name ":" (number-to-string name-idx) "*")))
(funcall oldfun scmd (get-buffer-create unique-name) nil)))
(advice-add #'async-shell-command :around #'cm/exec-shell)Eshell
(autoload 'eshell "eshell" "")
(defun cm/eshell-prompt ()
(concat user-login-name "@" system-name ":"
((lambda (p-lst)
(if (> (length p-lst) 4)
(concat
(mapconcat (lambda (elm) (if (string< "" elm)
(substring elm 0 1)
""))
(butlast p-lst (- (length p-lst) 3))
"/")
"/"
(mapconcat (lambda (elm) elm)
(last p-lst (- (length p-lst) 3))
"/"))
(mapconcat (lambda (elm) elm)
p-lst
"/")))
(split-string (abbreviate-file-name (eshell/pwd)) "/"))
" % "))
(defun eshell-new (name)
"Create a shell buffer named NAME."
(interactive "sEshell Name: ")
(let* ((bn (concat "*eshell:" name "*"))
(eb (get-buffer bn)))
(if eb
(switch-to-buffer eb)
(eshell)
(rename-buffer bn))))
(defun eshell-main ()
(interactive)
(eshell-new "main"))
; thanks byron, now using rc
;(global-set-key (kbd "<f7>") 'eshell-main)
(defalias 'enew 'eshell-new)
(put 'eshell 'disabled "Use eshell-new instead!\n")
(autoload 'ansi-color "ansi-color" t nil)
;(defun cm/eshell-handle-ansi-color ()
; (ansi-color-apply-on-region eshell-last-output-start
; eshell-last-output-end))
(setq eshell-directory-name "~/.emacs.d/eshell")
(setq eshell-prompt-function 'cm/eshell-prompt)
(setq eshell-prompt-regexp "^[^%#$\n]+ [%#$] ")
(setenv "EDITOR" "emacsclient")
(setenv "P4USER" "jmickey")
(setenv "P4PORT" cm/ironport-p4port)
(setenv "P4CONFIG" "P4ENV")
;(defun eshell/mm (&rest args)
; "A better version of my mm alias"
; (interactive)
; (eshell-parse-command "ssh marsarch \"cd $PWD\; " (eshell-flatten-list (append "\"" args))))
(defun unbind-symbol (symbol)
"Totally unbind SYMBOL.
This includes unbinding its function binding, its variable binding and its
property list."
(interactive "SSymbol: ")
(fmakunbound symbol)
(makunbound symbol)
(setf (symbol-plist symbol) nil))
(defun eshell/asc (cmd &rest args)
"Eshell async shell command, to get rid of double quotes"
(interactive)
(let* ((asc-buffer-name (concat "*asc:" cmd "*"))
(buffer (get-buffer-create (generate-new-buffer-name asc-buffer-name)))
(directory default-directory))
;; If will kill a process, query first.
(setq proc (get-buffer-process buffer))
(if proc
(if (yes-or-no-p "A command is running. Kill it? ")
(kill-process proc)
(error "Shell command in progress")))
(with-current-buffer buffer
(setq buffer-read-only nil)
;; Setting buffer-read-only to nil doesn't suffice
;; if some text has a non-nil read-only property,
;; which comint sometimes adds for prompts.
(let ((inhibit-read-only t))
(erase-buffer))
(display-buffer buffer)
(setq default-directory directory)
(setq proc (start-file-process-shell-command
asc-buffer-name
buffer cmd
(eshell-flatten-and-stringify args)))
(setq mode-line-process '(":%s"))
(require 'shell) (shell-mode)
(set-process-sentinel proc 'shell-command-sentinel)
;; Use the comint filter for proper handling of carriage motion
;; (see `comint-inhibit-carriage-motion'),.
(set-process-filter proc 'comint-output-filter))))
;; Stolen from http://www.emacswiki.org/cgi-bin/wiki.pl/EshellEnhancedLS
(eval-after-load "em-ls"
'(progn
(defun ted-eshell-ls-find-file-at-point (point)
"RET on Eshell's `ls' output to open files."
(interactive "d")
(find-file (buffer-substring-no-properties
(previous-single-property-change point 'help-echo)
(next-single-property-change point 'help-echo))))
(defun pat-eshell-ls-find-file-at-mouse-click (event)
"Middle click on Eshell's `ls' output to open files.
From Patrick Anderson via the wiki."
(interactive "e")
(ted-eshell-ls-find-file-at-point (posn-point (event-end event))))
(let ((map (make-sparse-keymap)))
(define-key map (kbd "RET") 'ted-eshell-ls-find-file-at-point)
(define-key map (kbd "<return>") 'ted-eshell-ls-find-file-at-point)
(define-key map (kbd "<mouse-2>") 'pat-eshell-ls-find-file-at-mouse-click)
(defvar ted-eshell-ls-keymap map))
(defadvice eshell-ls-decorated-name (after ted-electrify-ls activate)
"Eshell's `ls' now lets you click or RET on file names to open them."
(add-text-properties 0 (length ad-return-value)
(list 'help-echo "RET, mouse-2: visit this file"
'mouse-face 'highlight
'keymap ted-eshell-ls-keymap)
ad-return-value)
ad-return-value)))
(add-hook 'eshell-preoutput-filter-functions 'ansi-color-apply)
BBDB - Big Brother DataBase
Well integrated into Gnus, eventually just had to start using it! Borrowed this pretty heavily from somewhere, will document once I know what all these features really mean.(require 'bbdb)
;; uber failure
(require 'message)
(bbdb-initialize 'mail 'message)
(setq
bbdb-offer-save 1 ;; 1 means save-without-asking
bbdb-use-pop-up t ;; allow popups for addresses
bbdb-electric-p t ;; be disposable with SPC
bbdb-popup-target-lines 1 ;; very small
bbdb-dwim-net-address-allow-redundancy t ;; always use full name
bbdb-quiet-about-name-mismatches 2 ;; show name-mismatches 2 secs
bbdb-always-add-address t ;; add new addresses to existing...
;; ...contacts automatically
bbdb-canonicalize-redundant-nets-p t ;; x@foo.bar.cx => x@bar.cx
bbdb-completion-type nil ;; complete on anything
bbdb-complete-name-allow-cycling t ;; cycle through matches
;; this only works partially
bbbd-message-caching-enabled t ;; be fast
bbdb-use-alternate-names t ;; use AKA
bbdb-elided-display t ;; single-line addresses
;; auto-create addresses from mail
bbdb/mail-auto-create-p 'bbdb-ignore-some-messages-hook
bbdb-ignore-some-messages-alist ;; don't ask about fake addresses
;; NOTE: there can be only one entry per header (such as To, From)
;; http://flex.ee.uec.ac.jp/texi/bbdb/bbdb_11.html
'(( "From" . "no.?reply\\|DAEMON\\|daemon\\|facebookmail\\|twitter")))MML + org
(defun cm/org-mime-html-hook ()
(org-mime-change-element-style
"pre" (format "color: %s; background-color: %s; padding: 0.5em;"
"#E6E1DC" "#232323"))
(org-mime-change-element-style
"blockquote" "border-left: 2px solid gray; padding-left: 4px;"))
(add-hook 'org-mime-html-hook 'cm/org-mime-html-hook)
(add-hook 'message-mode-hook
(lambda ()
(local-set-key "\C-c\M-o" 'org-mime-htmlize)))
(add-hook 'org-mode-hook
(lambda ()
(local-set-key "\C-c\M-o" 'org-mime-org-buffer-htmlize)))Gnus
xdg-open!
(defun cm/advise-browse-url-can-use-xdg-open (fun &rest args)
(let ((res (apply fun args)))
(if (not res)
(and (getenv "DISPLAY")
(executable-find "xdg-open")
(executable-find "nohup"))
res)))
(advice-add #'browse-url-can-use-xdg-open :around #'cm/advise-browse-url-can-use-xdg-open)Uniquify
So useful, I think everyone should have this turned on.(require 'uniquify)
(setq uniquify-buffer-name-style 'post-forward)
(setq uniquify-after-kill-buffer-p t)
;; unrelated, but a nice spot for it
(defun uniquify-all-lines-region (start end)
"Find duplicate lines in region START to END keeping first occurrence."
(interactive "*r")
(save-excursion
(let ((end (copy-marker end)))
(while
(progn
(goto-char start)
(re-search-forward "^\\(.*\\)\n\\(\\(.*\n\\)*\\)\\1\n" end t))
(replace-match "\\1\n\\2")))))
(defun uniquify-all-lines-buffer ()
"Delete duplicate lines in buffer and keep first occurrence."
(interactive "*")
(uniquify-all-lines-region (point-min) (point-max)))Unhighlight all by default
The keybinding M-s h u by default only unhighlights one entry, and
then C-u M-s h u unhighlights everything. I’m going to swap these
two keybindings with the following:
(require 'hi-lock)
(defun cm/unhighlight-regexp-swap (arg)
(interactive "P")
(pcase arg
('(4)
(message "found 4, running nil")
(command-execute #'unhighlight-regexp))
('nil
(message "found nil, running t")
(unhighlight-regexp t))
(_
(message "found other(%s), running arg" arg)
(unhighlight-regexp arg))))
(substitute-key-definition 'unhighlight-regexp 'cm/unhighlight-regexp-swap hi-lock-map)
(substitute-key-definition 'unhighlight-regexp 'cm/unhighlight-regexp-swap search-map)Diff Before Save
I find myself often forgetting what I’ve changed in a file and what I haven’t. Obviously I use git, but I save files multiple times before committing, and can get a little lost. The challenges around auto-committing are pretty intense as well, as many intermediate states are basically completely irrelevant. I need more diligence in these areas.
Anyways, this just makes sure that I can easily diff the file I’m currently looking at with the file on disk. It’ll help me get back to sanity in many cases.
- TODO default to current buffer, no
RETrequired - TODO if not a file-based buffer, then ask for buffer nome
(global-set-key (kbd "C-c d") #'diff-buffer-with-file)Unwrap lines
(defun cm/unwrap-lines ()
(interactive)
(let ((fill-column 999999999999))
(fill-paragraph nil t)))External
Emacs Features
Ivy
The competitor to Helm. Supposedly simpler.. but I see it more like a Microsoft vs Linux situation. Luckily both are better than the Lisp vs C situation (they both chose lisp like adults).
(use-package ivy
:diminish ivy-mode
:config
(ivy-mode 1)
(setq ivy-use-virtual-buffers t)
(setq ivy-count-format ""))Hydra
Useful for hydras.
(use-package hyrda)Undo Tree
Undo tree is excellent! C-x u to browse. Now the larger question is
how it works with browse-kill-ring? I like both modes I guess.
(use-package undo-tree
:ensure t
:diminish undo-tree-mode
:init
(global-undo-tree-mode))Speedbar
I use sr-speedbar so it’s part of the same emacs frame.
(use-package sr-speedbar
:commands (sr-speedbar-open sr-speedbar-toggle)
:bind (("<f5>" . sr-speedbar-toggle)))Minimap
Got jealous, had to have it in emacs. Not so useful after all…(use-package minimap
:commands minimap-create)Word count
NaNoWriMo!(autoload 'word-count-mode "word-count"
"Minor mode to count words." t nil)
(global-set-key "\M-+" 'word-count-mode)IBuffer
Incredibly useful way to browse your buffers. (require 'ibuffer)
;; replace emac's default buffer list with the excellent ibuffer
(global-set-key (kbd "C-x C-b") 'ibuffer)
(define-ibuffer-sorter filename-or-dired
"Sort the buffers by their pathname."
(:description "filenames plus dired")
(string-lessp
(with-current-buffer (car a)
(or buffer-file-name
(if (eq major-mode 'dired-mode)
(expand-file-name dired-directory))
;; so that all non pathnames are at the end
"~"))
(with-current-buffer (car b)
(or buffer-file-name
(if (eq major-mode 'dired-mode)
(expand-file-name dired-directory))
;; so that all non pathnames are at the end
"~"))))
;; Add pathnam sorting, useful after 's m'
(define-key ibuffer-mode-map (kbd "s p") 'ibuffer-do-sort-by-filename-or-dired)
;; we both know this isn't true, but go with it.
(setq ibuffer-expert t)
(setq ibuffer-saved-filter-groups
'(("default"
("Work"
(or
(filename . "/google/")
(filename . "/work/")))
("GTD"
(filename . "/org/"))
("Mail"
(or (mode . mu4e-compose-mode)
(mode . mu4e-main-mode)
(mode . mu4e-headers-mode)
(mode . mu4e-org-mode)
(mode . mu4e-view-mode)
(mode . notmuch-search-mode)
(mode . notmuch-show-mode)
(mode . message-mode)
(mode . notmuch-message-mode)
(name . "bbdb")))
("Emacs"
(or (name . "*Messages*")
(name . "*scratch*")
(name . "*GNU Emacs*")
(name . "*Occur*")
(name . "*Backtrace*")
(name . "*Help*")
(name . "tramp/ssh")
(name . "tramp/sudo")
(name . "*Calculator*")
(name . "*Calc Trail*")
(name . "*Diff*"))))))
(setq ibuffer-formats
'((mark modified read-only " "
(name 50 50 :left :elide)
" "
(size 9 -1 :right)
" "
(mode 16 16 :left :elide)
" " filename-and-process)
(mark " "
(name 30 -1)
" " filename)))
(add-hook 'ibuffer-mode-hook
(lambda ()
(ibuffer-switch-to-saved-filter-groups "default")))
Browse Kill Ring
Navigate visually through the entire kill ring.(autoload 'browse-kill-ring "browse-kill-ring" "")
(global-set-key (kbd "M-y") 'browse-kill-ring)Expand region
If you have a region selected, typingC-= will expand the selection
out semantically.
(autoload 'expand-region "expand-region" "")
(global-set-key (kbd "C-=") 'er/expand-region)Ace Jump
(use-package
ace-jump-mode
:bind ("C-." . ace-jump-mode))Hilight line
;; Default hl
(global-hl-line-mode t)
(make-variable-buffer-local 'global-hl-line-mode)
;; wombat and others set underlines. they are assholes.
(set-face-underline hl-line-face nil)
(set-face-background 'highlight "#303030")
(set-face-foreground 'highlight nil)Projectile
(use-package projectile
:diminish projectile-mode
:init
(put 'projectile-project-name 'safe-local-variable 'stringp)
(put 'projectile-project-compilation-cmd 'safe-local-variable 'stringp)
(put 'projectile-use-git-grep 'safe-local-variable 'booleanp)
:config
(projectile-global-mode)
(setq projectile-completion-system 'ivy)
(setq projectile-indexing-method 'native)
(setq projectile-enable-caching t)
;; this replaces the old `projectile-compile-project' to use the
;; project name in the compilation buffer. Let's me run all ze
;; compilations!
(defun cm/projectile-compile-project (arg &optional dir)
"Run project compilation command, using the project name
Normally you'll be prompted for a compilation command, unless
variable `compilation-read-command'. You can force the prompt
with a prefix ARG."
(interactive "P")
(let* ((project-root (if dir
dir
(projectile-project-root)))
(default-directory project-root)
(default-cmd (projectile-compilation-command project-root))
(compilation-cmd (if (or compilation-read-command arg)
(projectile-read-command "Compile command: "
default-cmd)
default-cmd)))
(puthash project-root compilation-cmd projectile-compilation-cmd-map)
(save-some-buffers (not compilation-ask-about-save)
(lambda ()
(projectile-project-buffer-p (current-buffer)
project-root)))
(with-current-buffer
(compilation-start compilation-cmd nil '(lambda (x) (concat "*compilation:" (projectile-project-name) "*")))
(setq-local projectile-project-name (projectile-project-name)))))
(advice-add 'projectile-compile-project :override #'cm/projectile-compile-project))Multiple Cursors
(use-package multiple-cursors
:bind (("C->" . mc/mark-next-like-this)
("C-<" . mc/mark-previous-like-this)
("C-c C->" . mc/mark-all-like-this)
("C-S-<mouse-1>" . mc/add-cursor-on-click)))fill column indicator (fci)
This draws a vertical line at the fill column. Nice for languages
without things like gofmt.
(use-package fill-column-indicator
:commands (fci-mode))Mixed Pitch (disabled, buggy on org files)
This makes things just a tad nicer to read.
;; (use-package mixed-pitch
;; :commands mixed-pitch-mode
;; :diminish mixed-pitch-mode
;; :init
;; (add-hook 'org-mode-hook #'mixed-pitch-mode)
;; :config
;;; (setq mixed-pitch-variable-pitch-cursor nil)
;;; (add-to-list 'mixed-pitch-fixed-pitch-faces 'org-todo)
;;; (add-to-list 'mixed-pitch-fixed-pitch-faces 'org-done)
;;; (add-to-list 'mixed-pitch-fixed-pitch-faces 'org-link)
;;)Major Modes
Ledger
I use ledger to track my finances. I have it align amounts and use ISO dates, but other than that pretty normal config(use-package
ledger-mode
:mode ("\\.ledger$"
"\\.journal$")
:init
(setq ledger-post-auto-adjust-amounts t)
(setq ledger-use-iso-dates t))Markdown
(use-package
markdown-mode
:mode "\\.\\(md\\|markdown\\|mdwn\\)$")Evil
Activate Evil!
I have become one of them :/;(require 'evil)
;
;(evil-mode 1)Evil Ace Jump
Let’s me use ace jump e’erywhere;(define-key evil-motion-state-map (kbd "SPC") #'evil-ace-jump-word-mode)
;(define-key evil-motion-state-map (kbd "C-SPC") #'evil-ace-jump-char-mode)
;
;(define-key evil-operator-state-map (kbd "SPC") #'evil-ace-jump-word-mode) ; similar to f
;(define-key evil-operator-state-map (kbd "C-SPC") #'evil-ace-jump-char-mode) ; similar to t
;(define-key evil-operator-state-map (kbd "M-SPC") #'evil-ace-jump-char-to-mode)
;
;;; different jumps for different visual modes
;(defadvice evil-visual-line (before spc-for-line-jump activate)
;(define-key evil-motion-state-map (kbd "SPC") #'evil-ace-jump-word-mode))
;
;(defadvice evil-visual-char (before spc-for-char-jump activate)
;(define-key evil-motion-state-map (kbd "SPC") #'evil-ace-jump-word-mode))
;
;(defadvice evil-visual-block (before spc-for-char-jump activate)
;(define-key evil-motion-state-map (kbd "SPC") #'evil-ace-jump-word-mode))
;(evil-set-initial-state 'shell-mode 'emacs)
;(evil-set-initial-state 'org-mode 'emacs)For some reason that does not include evil-local-mode.
(unless (boundp 'evil-local-mode)
(autoload 'evil-local-mode "evil" "Toggle evil in single buffer" t))Valgrind
; Based on compile.el included with Emacs
; and ideas from http://tromey.com/blog/?p=342
; compile.el is GPL, so this is too.
(require 'compile "compile")
(defgroup valgrind nil
"Run valgrind as inferior of Emacs, parse error messages."
:group 'tools
:group 'processes)
(defcustom valgrind-command "valgrind --leak-check=full "
"*Last shell command used to run valgrind; default for next valgrind run.
Sometimes it is useful for files to supply local values for this variable.
You might also use mode hooks to specify it in certain modes, like this:
(add-hook 'c-mode-hook
(lambda ()
(unless (or (file-exists-p \"makefile\")
(file-exists-p \"Makefile\"))
(set (make-local-variable 'valgrind-command)
(concat \"make -k \"
(file-name-sans-extension buffer-file-name))))))"
:type 'string
:group 'valgrind)
;; History of compile commands.
(defvar valgrind-history nil)
(defun valgrind (command)
"Run valgrind.
Runs COMMAND, a shell command, in a separate process asynchronously
with output going to the buffer `*valgrind*'.
You can then use the command \\[next-error] to find the next error message
and move to the source code that caused it."
(interactive
(if (or compilation-read-command current-prefix-arg)
(list (read-from-minibuffer "Valgrind command: "
(eval valgrind-command) nil nil
'(valgrind-history . 1)))
(list (eval valgrind-command))))
(unless (equal command (eval valgrind-command))
(setq valgrind-command command))
(compilation-start command t))PlantUML
Get the jarfile in the correct place… (setq cm/plantuml-paths
(list
(concat (getenv "HOME") "/bin/plantuml.jar")
"/usr/share/plantuml/plantuml.jar"))
(setq plantuml-jar-path
(reduce
(lambda (x y) (if (and x (file-exists-p x)) x y))
cm/plantuml-paths))
(setenv "GRAPHVIZ_DOT" "/usr/bin/dot")Scheme
Paredit is an amazing minor mode for editing lisp, but it is a bit complex. I turn it on for scheme, but not elisp as usually when I’m hacking on elisp I want the lowest barrier to entry.(add-hook 'scheme-mode-hook
'(lambda ()
(paredit-mode 1)))Paredit
(use-package paredit)Markdown
(autoload 'markdown-mode "markdown-mode.el"
"Major mode for editing Markdown files" t)
(add-hook 'markdown-mode-hook '(lambda ()
(flyspell-mode 1)
(auto-fill-mode 1)))
;; autoload
(add-to-list 'auto-mode-alist '("\\.mdwn$" . markdown-mode))Haskell
(add-to-list 'auto-mode-alist '("\\.hs$" . haskell-mode))
(add-hook 'haskell-mode-hook 'turn-on-haskell-indent)
(add-hook 'haskell-mode-hook 'turn-on-haskell-doc-mode)
(setq haskell-hoogle-program "hoogle")Go
Fuck GOPATH and everything in it
So emacs doesn’t handle project specific environment variables. Which sucks horribly, and I don’t blame any of the project tools for not addressing it, as there are no great answers.The fact that the golang project has made it a prerequisite that so many are set, and that there are rarely cli overrides for them is really a fault of their own.
(use-package go-mode
:mode "\\.go$"
:config
(add-hook 'go-mode-hook 'cm/go-mode-hook))
(defun cm/go-mode-hook ()
(interactive)
(require 'go-oracle)
(setq go-oracle-command (concat (getenv "HOME") "/bin/oracle"))
(go-oracle-mode)
(subword-mode 1)
(diminish 'subword-mode)
(setq fill-column 80)
(fci-mode)
(diminish 'go-oracle-mode)
(setq imenu-generic-expression
'(("type" "^type *\\([^ \t\n\r\f]*\\)" 1)
("func" "^func *\\(.*\\) {" 1)))
(imenu-add-to-menubar "Index")
(font-lock-mode 1)
(add-hook 'before-save-hook #'gofmt-before-save))Ruby
Lisp has kind of taken over from Ruby. Whether that’s Scheme or Common Lisp seems to be the current mental debate.(autoload 'ruby-mode "ruby-mode"
"Mode for editing ruby source files")
(add-hook 'ruby-mode-hook 'turn-on-font-lock)
(add-to-list 'interpreter-mode-alist '("ruby" . ruby-mode))
(add-to-list 'auto-mode-alist '("\\.rb$" . ruby-mode))C
My C settings.(require 'compile)
(defun* get-closest-pathname (&optional (file "*akefile") (dir default-directory))
"Determine the pathname of the first instance of FILE starting
from the current directory towards root. This may not do the
correct thing in presence of links. If it does not find FILE,
then it shall return the name of FILE in the current
directory, suitable for creation"
(let ((root (expand-file-name "/")))
(loop
for d = dir then (expand-file-name ".." d)
if (file-expand-wildcards (expand-file-name file d))
return (car (file-expand-wildcards (expand-file-name file d)))
if (equal d root)
return nil)))
(defun cm/make-c++-header ()
(interactive)
(c++-mode)
(add-file-local-variable-prop-line 'mode 'c++))
(c-add-style "mars" '("linux"))
(defconst iridium-c++-style
'((c-basic-offset . 4)
(c-comment-only-line-offset . 4)
(c-hanging-braces-alist . ((substatement-open before)
(brace-list-open before)))
(c-hanging-colons-alist . ((member-init-intro after)
(inher-intro after)
(case-label after)
(label after)
(access-label after)))
(c-offsets-alist . ((defun-open . 0)
(defun-block-intro . 4)
(label . 0)
(inclass . +)
(case-label . +)
(access-label . 0)
(brace-list-intro . 4)
(substatement-open . 0)
(member-init-intro . 8)
(statement-case-open . 0)
(statement-block-intro . 4)
(class-open . 0)
(inline-open . 0)
(innamespace . 4)
(namespace-open . 0)
(comment-intro . 0)
(c . 1)
(inher-intro 8)
(class-close . 0)
(namespace-close . 0)
(func-decl-cont . 8))))
"Iridium style")
(add-to-list 'auto-mode-alist '("\\.h\\'" . c++-mode))
(defun cm/iridium-c-hook ()
(interactive)
(c-add-style "iridium-c++" iridium-c++-style t)
(setq indent-tabs-mode nil)
(setq comment-multi-line t)
(local-set-key (kbd "C-c o") 'ff-find-other-file)
;(helm-gtags-mode)
(setq fill-column 90))
(defun cm/goog-c-hook ()
(setq indent-tabs-mode nil)
(setq comment-multi-line t)
(setq fill-column 80)
(local-set-key (kbd "C-c o") 'ff-find-other-file))
(defun cm/work-c-hook ()
(cm/goog-c-hook))
(add-hook 'c++-mode-hook 'cm/work-c-hook)
(add-hook 'cc-mode-hook 'cm/work-c-hook)
(add-hook 'c-mode-hook 'cm/work-c-hook)
(defun mars-c-hook ()
(interactive)
(c-set-style "mars")
(require 'auto-complete)
(setq tab-width 8)
(setq indent-tabs-mode t)
(setq tab-stop-list
'(8 16 24 32 40 48 56 64 72 80 88 96 104 112 120))
(setq fill-column 80)
(setq-default c-basic-offset 8)
(setq show-trailing-whitespace t)
(setq c-tab-always-indent t)
(linum-mode 1)
(setq comment-multi-line t)
(local-set-key (kbd "C-c o") 'ff-find-other-file)
;; (gtags-mode 1) ; no more! going to helm!
;; (helm-gtags-mode)
(set (make-local-variable 'compilation-directory-matcher)
'("\\(?:\\(?:Entering\\|Leavin\\(g\\)\\) directory
`\\(.+\\)'$\\)\\|\\(?:[^]^[]*\\][[:space:]]*\\(\\(?:[[:alnum:]]*/\\)+\\)\\)\\|\\(?:^\\(\\[\\)\\)"
(2 . 1) (3 . 9) (4 . 4)))
(set (make-local-variable 'compile-command) "~/bin/emacs-mars-compile")
(add-to-list 'ac-sources '(ac-source-gtags
ac-source-semantic
ac-source-words-in-buffer))
(auto-complete-mode -1))
GNU Plot
Get some fancy graphs going. I mostly use R, but this is nice when other people use gnuplot.(autoload 'gnuplot-mode "gnuplot" "gnuplot major mode" t)
(autoload 'gnuplot-make-buffer "gnuplot" "open a buffer in gnuplot mode" t)
(add-to-list 'auto-mode-alist '("\\.gp$" . gnuplot-mode))Magit
Because I switched to Magit! Works well, and is fairly canonical at this point. I don’t understand vc-mode that well yet, so I really just use magit-status and magit-log.magit-log is missing the commiter annotation, I need to fix that.
(use-package magit
:init
;; super incredibly slow thing that would be good in theory but
;; implementation is not there.
(setq magit-revert-buffers nil)
(setq magit-push-always-verify nil))Custom Commit Message Formatting
Sort files by spelling. I figure the internal ordering of each paragraph should be fine. I’m still working on a cm/gnu-fill-paragraph that works on these commit message regions.This regex finds the first line that begins with an asterix.
(defvar cm/gnu-line-start "^\* .*$")sort-subr expects two functions to help it find the boundaries of
records. nextrec finds the beginning of the next record, unless
there are no more records and then it should be pointing at the end of
the buffer (eobp).
(defun cm/gnu-line-next ()
(if (not (eobp))
(forward-line 1))
(while (and (not (eobp)) (not (looking-at cm/gnu-line-start)))
(forward-line 1))
(end-of-line)
(if (not (eobp))
(beginning-of-line)))endrec finds the end of the current record. In the commit message
case this means finding the next line that starts with an asterix,
then going back to the previous line’s end. This handles commit
messages that have multiple functions/lines describing a file’s
changes.
(defun cm/gnu-line-end ()
(if (not (eobp))
(forward-line 1))
(while (and (not (eobp)) (not (looking-at cm/gnu-line-start)))
(forward-line 1))
(if (not (eobp)) (forward-line -1))
(end-of-line))Finally, we get to the part where we actually call sort-subr. Pretty
standard implementation, and mostly lifted from sort-paragraphs in
sort.el.gz.
(defun cm/sort-gnu-lines (reverse beg end)
(interactive "P\nr")
(save-excursion
(save-restriction
(narrow-to-region beg end)
(goto-char (point-min))
(sort-subr reverse 'cm/gnu-line-next 'cm/gnu-line-end))))Fix up the idea of “creating” gnu lines
; this works well with my git-commit after save hook and rebasing
;(defun cm/create-gnu-lines (reverse beg end)
; (interactive "P\nr")
; (save-excursion
; (save-restriction
; (narrow-to-region beg end)
; (goto-char (point-min))
; (
(fset 'cm/create-gnu-lines
"* \C-a\C-n\C-k\C-k\C-k\C-k")I don’t have a good keybinding for this yet, but I will figure something out :P
Esperanto
This is heavily borrowed from the spanish mode, works wonders.(load-library "esperanto")Mode Compile
Friendlier compilation support, tries to guess what the compilation command should be. I haven’t found this to be incredibly helpful unless you are using the default build systems (make, pdflatex, etc).(global-set-key "\C-cc" 'compile)YAML
More ruby days.(autoload 'yaml-mode "yaml-mode" "Yaml editing mode" t)
(add-to-list 'auto-mode-alist '("\\.yml$" . yaml-mode))
(add-to-list 'auto-mode-alist '("\\.yaml$" . yaml-mode))HAML
No seriously, I wrote lots of Ruby.(autoload 'haml-mode "haml-mode" "" t)
(add-hook 'haml-mode-hook '(lambda ()
(setq indent-tabs-mode nil)))
(add-to-list 'auto-mode-alist '("\\.haml$" . haml-mode))
(add-to-list 'auto-mode-alist '("\\.sass$" . sass-mode))Tramp
(require 'tramp-loaddefs)
(require 'tramp)
(eval-after-load 'tramp
(progn
(setq tramp-histfile-override nil)
(setq tramp-use-ssh-controlmaster-options nil) ;; use ssh config
(setq tramp-verbose 6)))GNU Global
Thank you Britt.; (require 'gtags-autoloads)
(defun my-gtags-settings ()
"Settings for gtags."
;; Key bindings.
(define-prefix-command 'gtags-keymap)
(define-key global-map (kbd "C-c g") 'gtags-keymap)
(define-key gtags-mode-map (kbd "C->") 'gtags-find-tag-from-here)
(define-key gtags-mode-map (kbd "C-<") 'gtags-pop-stack)
(define-key gtags-mode-map (kbd "C-c g s") 'gtags-find-symbol)
(define-key gtags-mode-map (kbd "C-c g t") 'gtags-find-tag)
(define-key gtags-mode-map (kbd "C-c g r") 'gtags-find-rtag)
(define-key gtags-mode-map (kbd "C-c g p") 'my-gtags-find-file)
(define-key gtags-mode-map (kbd "C-c g v") 'gtags-visit-rootdir)
(define-key gtags-mode-map [mouse-2] 'gtags-find-tag-by-event)
(define-key gtags-mode-map [mouse-3] 'gtags-pop-stack)
(define-key gtags-select-mode-map (kbd "n") 'next-line)
(define-key gtags-select-mode-map (kbd "p") 'previous-line)
(define-key gtags-select-mode-map (kbd "RET") 'gtags-select-tag)
(define-key gtags-select-mode-map (kbd "C-<") 'gtags-pop-stack)
(define-key gtags-select-mode-map (kbd "C->") 'gtags-select-tag)
(define-key gtags-select-mode-map (kbd "q") 'gtags-pop-stack)
(define-key gtags-select-mode-map [mouse-2] 'gtags-select-tag-by-event)
(define-key gtags-select-mode-map [mouse-3] 'gtags-pop-stack)
;; Highlight gtags item line.
(add-hook 'gtags-select-mode-hook '(lambda () (hl-line-mode 1)))
;; Update gtags data after save file.
(defun gtags-update ()
"Update gtags data."
(interactive)
(start-process "gtags-update" nil "global" "-u"))
; (add-hook 'after-save-hook 'gtags-update) ;ahh, no
;; visit current file under cursor.
(defun my-gtags-find-file ()
"Gtags find file, and jump to last exit position."
(interactive)
(gtags-find-file)
(pop-global-mark))
;; find current header file under cursor.
(defun my-gtags-find-this-file ()
"Gtags find current header file under cursor."
(interactive)
(let (tagname)
(setq tagname (concat (current-word) ".h"))
(gtags-push-context)
(gtags-goto-tag tagname "Po"))
(pop-global-mark))
(define-key gtags-mode-map [M-mouse-2] 'my-gtags-find-this-file)
)
; (eval-after-load "gtags"
; '(my-gtags-settings))Android
(autoload 'android-mode "android-mode.el" "Android minor mode" t)
(autoload 'android "/opt/android-sdk/tools/lib/android.el" "Google provided android emacs" t)Python
(add-hook 'python-mode-hook
'(lambda ()
(setq show-trailing-whitespace t)))simple httpd
This is a mode that provides a full http server in elisp. It does default to serving~/public_html, so I turn that off here.
(use-package
simple-httpd
:commands (httpd-start httpd-serve-directory)
:config
(setq httpd-root "")
(setq httpd-serve-files nil))PDF Tools
This adds much better support for pdfs in emacs.
(use-package pdf-tools
:pin manual
:config
(pdf-tools-install))Programs
Elfeed
elfeed is a great RSS reader in the style of notmuch. Super useful.
(use-package elfeed
:commands elfeed
:bind ("C-c f" . elfeed)
:config
(elfeed-load-opml (concat (getenv "HOME") "/config/feeds.opml")))EMMS - Emacs MultiMedia System
Now w/mopidy(require 'emms-setup)
(emms-standard)
(require 'emms-browser)
(require 'emms-player-mpd)
(add-to-list 'emms-info-functions 'emms-info-mpd)
(add-to-list 'emms-player-list 'emms-player-mpd)
(require 'emms-volume)
(setq emms-volume-change-function 'emms-volume-mpd-change)
(setq emms-info-asynchronosly t)
(add-hook 'emms-player-started-hook 'emms-show)
(setq emms-show-format "NP: %s")
(setq emms-mode-line-icon-before-format ""
emms-mode-line-format " %s"
emms-mode-line-icon-color "blue")
(if (executable-find "find")
(setq emms-source-file-directory-tree-function 'emms-source-file-directory-tree-find))
;; reserve a key namespace for emms
(global-set-key (kbd "C-c m p") 'emms-pause)
(global-set-key (kbd "C-c m m") 'emms-smart-browse)ERC
What I generally end up using for irc, but weechat always tends to win me over.(require 'erc)(setq erc-user-full-name "codemac")
(setq erc-email-userid "j@codemac.net")
(setq erc-nick "codemac")
(setq erc-prompt-for-password nil)
;; ERC Time stamps
(setq erc-timestamp-only-if-changed-flag t)
(setq erc-timestamp-format "[%H:%M:%S] ")
(setq erc-insert-timestamp-function 'erc-insert-timestamp-left)
;; Auto-fill (static size so log files look decent)
(setq erc-fill-column 80)
(setq erc-fill-function 'erc-fill-static)
(setq erc-fill-static-center 15)
(setq erc-join-buffer 'bury)
(setq erc-track-exclude-types '("JOIN" "NICK" "PART" "QUIT"))
;; Auto join the given channels
(erc-autojoin-mode t)
(setq erc-autojoin-channels-alist cm/erc-autojoin-channels-alist)
(setq erc-server-auto-reconnect t)
(setq erc-server-reconnect-attempts 5)
(setq erc-server-reconnect-timeout 5)
;; Some other settings
(setq erc-max-buffer-size 20000)
(setq erc-track-showcount t)
(setq erc-auto-query 'bury) ; Private messages go to a hidden buffer
(setq erc-autojoin-timing 'ident) ; wait for the delay (or ident resolution)
(setq erc-autojoin-delay 15)
;; Setup ERC buffers
(defun cm/erc-hook ()
"Correctly configure ERC buffers"
(auto-fill-mode 0)
(setq truncate-lines nil)
(add-to-list 'erc-modules 'scrolltobottom)
(add-to-list 'erc-modules 'truncate)
(add-to-list 'erc-modules 'hl-nicks)
(add-to-list 'erc-modules 'image)
(erc-update-modules))
(defun cm/erc-begin-bitlbee ()
(interactive)
(erc :server "localhost" :port 6667))
(defun cm/erc-begin-freenode ()
(interactive)
(erc-ssl :server "chat.freenode.net" :port 6697
:nick "codemac" :password cm/freenode-password))
(add-hook 'erc-mode-hook 'cm/erc-hook)
(add-hook 'erc-join-hook 'bitlbee-netrc-identify)
;; set up keys for erc
Bitlbee Support
(defun bitlbee-netrc-identify ()
"Auto-identify for Bitlbee channels using authinfo or netrc.
The entries that we look for in netrc or authinfo files have
their 'port' set to 'bitlbee', their 'login' or 'user' set to
the current nickname and 'server' set to the current IRC
server's name. A sample value that works for authenticating
as user 'keramida' on server 'localhost' is:
machine localhost port bitlbee login keramida password supersecret"
(interactive)
(when (string= (buffer-name) "&bitlbee")
(let* ((secret (plist-get (nth 0 (auth-source-search :max 1
:host erc-server
:user (erc-current-nick)
:port "bitlbee"))
:secret))
(password (if (functionp secret)
(funcall secret)
secret)))
(erc-message "PRIVMSG" (concat (erc-default-target) " " "identify" " " password) nil))))
;; Enable the netrc authentication function for &biblbee channels.
;(add-hook 'erc-join-hook 'bitlbee-netrc-identify)EXWM - Emacs as X window manager
(when (equal x-resource-name "emacs-as-x")
(require 'exwm)
(require 'exwm-config)
(require 'exwm-randr)
(setq exwm-randr-workspace-output-plist '(0 "eDP1" 1 "HDMI2"))
(exwm-randr-enable)
;; I swap C-t and C-x, so this is a very useful thing to also have
;; as a prefix
(add-to-list 'exwm-input-prefix-keys ?\C-t)
(add-hook 'exwm-update-class-hook
(lambda ()
(unless (or (string-prefix-p "sun-awt-X11-" exwm-instance-name)
(string= "gimp" exwm-instance-name))
(exwm-workspace-rename-buffer exwm-class-name))))
(add-hook 'exwm-update-title-hook
(lambda ()
(when (or (not exwm-instance-name)
(string-prefix-p "sun-awt-X11-" exwm-instance-name)
(string= "gimp" exwm-instance-name))
(exwm-workspace-rename-buffer exwm-title))))
(setq exwm-workspace-number 10)
(defun cm/exwm-workspace-next ()
(interactive)
(exwm-workspace-switch (mod (+ 1 exwm-workspace-current-index) exwm-workspace-number)))
(defun cm/exwm-workspace-previous ()
(interactive)
(exwm-workspace-switch (mod (- exwm-workspace-current-index 1) exwm-workspace-number)))
(exwm-input-set-key (kbd "s-r") #'exwm-reset)
(exwm-input-set-key (kbd "s-n") #'cm/exwm-workspace-next)
;; s-p does not work on lenovo :( so we'll do s-o and s-n so they're
;; opposite hands for directions in dvorak.
(exwm-input-set-key (kbd "s-o") #'cm/exwm-workspace-previous)
(exwm-input-set-simulation-keys
'(([?\C-b] . left)
([?\C-f] . right)
([?\C-p] . up)
([?\C-n] . down)
([?\C-a] . home)
([?\C-e] . end)
([?\M-v] . prior)
([?\C-v] . next)
([?\C-d] . delete)
([?\C-k] . (S-end delete))))
(dotimes (i exwm-workspace-number)
(exwm-input-set-key (kbd (format "s-%d" i))
`(lambda ()
(interactive)
(exwm-workspace-switch-create ,i))))
(exwm-enable))Flymake
;; stolen from http://www.reddit.com/r/emacs/comments/i05v3/emacs_and_pylint/c1ztm6x (user kanak on /r/emacs)
(require 'flymake)
(setq *cm/flychecker-directory* "~/.emacs.d/flycheck")
(defmacro def-flymake-init (mode checker-file)
"Writes a function called flymake-MODE-init which contains the usual boilerplate for a default flymake initialization."
`(defun ,(intern (format "flymake-%s-init" mode)) ()
(let* ((temp-file (flymake-init-create-temp-buffer-copy
'flymake-create-temp-inplace))
(local-file (file-relative-name
temp-file
(file-name-directory buffer-file-name))))
(list (expand-file-name ,checker-file *cm/flychecker-directory*) (list local-file)))))
(defmacro def-flymake-cleanup (mode extlist)
"Writes a function called flymake-MODE-cleanup which removes files with specified extensions in current directory."
`(defun ,(intern (format "flymake-%s-cleanup" mode)) ()
(when flymake-temp-source-file-name
(let* ((temp-files
(mapcar (lambda (ext)
(concat
(file-name-sans-extension flymake-temp-source-file-name) ext))
,extlist)))
(dolist (f temp-files)
(when (file-exists-p f)
(flymake-safe-delete-file f)))))
(flymake-simple-cleanup)))
(def-flymake-init "python" "~/.emacs.d/flycheckbin/pychecker.sh")
(add-to-list 'flymake-allowed-file-name-masks '("\\.py\\'" flymake-python-init))
;; flyspell
(setq ispell-program-name "aspell")
(setq ispell-list-command "list")
(setq ispell-extra-args '("--sug-mode=ultra"))Geiser
Let’s get our scheme on!(require 'geiser-install nil t)Jabber
Directly use jabber with elisp! It’s painfully slow, and the single threaded nature of emacs means it turns out to be quite a failure if you are trying to edit anything. (autoload 'jabber-connect-all "jabber" "" t)
;; Show my status in the header along with theirs! woo!
(eval-after-load 'jabber
(progn
(setq jabber-chat-header-line-format
'(" " (:eval (jabber-jid-displayname jabber-chatting-with))
" " (:eval (jabber-jid-resource jabber-chatting-with)) "\t";
(:eval (let ((buddy (jabber-jid-symbol jabber-chatting-with)))
(propertize
(or
(cdr (assoc (get buddy 'show) jabber-presence-strings))
(get buddy 'show))
'face
(or (cdr (assoc (get buddy 'show) jabber-presence-faces))
'jabber-roster-user-online))))
"\t" (:eval (get (jabber-jid-symbol jabber-chatting-with) 'status))
(:eval (unless (equal "" *jabber-current-show*)
(concat "\t You're " *jabber-current-show*
" (" *jabber-current-status* ")")))))
;; Open urls!
(add-hook 'jabber-chat-mode-hook 'goto-address)
;; fun keybindings!
(defun my-jabber-chat-delete-or-bury ()
(interactive)
(if (eq 'jabber-chat-mode major-mode)
(condition-case e
(delete-frame)
(error
(if (string= "Attempt to delete the sole visible or iconified frame"
(cadr e))
(bury-buffer))))))
; (define-key jabber-chat-mode-map [escape] 'my-jabber-chat-delete-or-bury)
(define-key mode-specific-map "jr"
(lambda ()
(interactive)
(switch-to-buffer "*-jabber-*")))
(define-key mode-specific-map "jc"
'(lambda ()
(interactive)
(call-interactively 'jabber-connect)))
(define-key mode-specific-map "jd"
'(lambda ()
(interactive)
(call-interactively 'jabber-disconnect)))
(define-key mode-specific-map "jj"
'(lambda ()
(interactive)
(call-interactively 'jabber-chat-with)))
(define-key mode-specific-map "ja"
'(lambda ()
(interactive)
(jabber-send-presence "away" "" 10)))
(define-key mode-specific-map "jo"
'(lambda ()
(interactive)
(jabber-send-presence "" "" 10)))
(define-key mode-specific-map "jx"
'(lambda ()
(interactive)
(jabber-send-presence "xa" "" 10)))))
;;;; hipchat support
(setq ssl-program-name "gnutls-cli"
ssl-program-arguments '("--insecure" "-p" service host)
ssl-certificate-verification-policy 1)
;; Connect using jabber.el
;; M-x jabber-connect <RET>
;; Config
(setq jabber-account-list '(("75698_604162@chat.hipchat.com")))
(defvar hipchat-number "75698")
(defvar hipchat-nickname "Jeff Mickey")
;; Join a room
(defun cm/hipchat-join (room)
(interactive "sRoom name: ")
(jabber-groupchat-join
(jabber-read-account)
(concat hipchat-number "_" room "@conf.hipchat.com")
hipchat-nickname
t))
;; Mention nicknames in a way that HipChat clients will pickup
(defun hipchat-mention (nickname)
(interactive
(list (jabber-muc-read-nickname jabber-group "Nickname: ")))
(insert (concat "@\"" nickname "\" ")))
(defvar cm/hipchat-rooms '())
(defun cm/hipchat-joinall ()
(interactive)
(rcirc-cmd-join (mapconcat 'identity cm/hipchat-rooms ",") (rcirc-buffer-process (get-buffer "&bitlbee@localhost")) nil))Muse
Good for person wikis, however I pretty much exclusively useorg-blog=/=org-export now.
(add-hook 'muse-mode-hook '(lambda ()
(footnote-mode 1)
(flyspell-mode 1)
(auto-fill-mode 1)))
;; My wiki's!
;;(setq muse-project-alist
;; '(("Personal Miki" ("~/miki/src" :default "index")
;; (:base "html" :path "~/miki/html"))))Org
Holy god. Let it begin.Initialize org mode
I add a*.org regex to the auto-mode-alist, start up org-protocol
so I can use my Firefox bookmark and emacsclient integration, and
org-mouse in case I already have a pointing device. I imagine
org-mouse will be more useful when I try and run emacs on a tablet.
(add-to-list 'auto-mode-alist '("\\.org$" . org-mode))
(require 'org-loaddefs)
(require 'org)
(require 'org-protocol)
(require 'org-mouse)
(require 'org-notmuch)Org Agenda
Receipt Agenda
(defun org-receipt-agenda (match)
(setq org-agenda-include-all-todo nil
org-agenda-span 7
org-agenda-show-all-dates t)
(org-agenda-list))Custom agendas
Set up the styling. These are headers and footers that make it nicer to see my review adgendas, and easier on the eyes to follow. (setq cm/org-sort-style '((org-agenda-skip-function '(org-agenda-skip-entry-if 'scheduled))
(org-agenda-sorting-strategy
'((agenda habit-down time-up priority-down category-keep)
(todo tsia-up)
(tags tsia-up)
(search tsia-up)))))
(defun cm/review-section (title)
`((org-agenda-overriding-header ,(concat "########## " title " ##################################################"))))
(defun cm/review-header (title)
`((org-agenda-overriding-header ,(concat " " title))))
;; this dynamic title sizing doesn't work because the org-agenda
;; buffer is created when org-agenda is called. :/
;; (let* ((basewidth (/ (- (window-body-width) (+ (length title) 6)) 4.0))
;; (leftside (make-string (floor basewidth) ?+))
;; (rightside (make-string (ceiling basewidth) ?+)))
;; `((org-agenda-overriding-header ,(concat leftside " " title " " rightside)))))
(defun cm/org-ctx-agenda (key ctx title)
(let ((ctxlist (if (listp ctx) ctx (list ctx))))
`(,(concat "c" key) ,title
((tags-todo ,(mapconcat #'(lambda (x) (concat x "-TODO=\"PROJECT\"-BLOCKED=\"t\"-TODO=\"WAITING\"")) ctxlist "|")))
,cm/org-sort-style)))The org agenda. It’s mostly for gtd usage, so I have the following views:
d- “Day” view, useful for use throughout the day when I’ve scheduled most things and marked tasks as “NEXT”
i- inbox (anything without a tag, usually from orgmobile)
n- “next” agenda (Rarely used as this is also covered in the day view, just super flagged items I need to look into)
r- Review commands, currently there is ‘r d’ for review a day and ‘r w’ for review a week. These hopefully will get much more sophisticated as part of my review process.
p- Projects list. The projects list in GTD is one of the most important lists, so keeping this up to date and reviewed is very important. I’d love to add something like ‘levels of focus’ agendas for each, but it’s just not a practical way to review things that are done so infrequently.
c- Context commands, there are several, but the highlights are
they use the same keys as the relevant tagging short keys
for filing, so my brain uses them easily. I can finally move
INTERNETfromvtoiwith this change, but the muscle memory is already set, so we’ll have to wait and see on that one. x- Checklist agenda, these are largely for pre-flight checklists that I maintain. Not sure how to integrate them into my whole emacs / org-mode flow just yet.
(defun cm/day-agendas ()
`(("d" "Full Day Agenda"
((agenda "Today" ((org-agenda-span 1)))
(todo "NEXT" ,cm/org-sort-style)
(tags-todo "-TODO=\"PROJECT\"-TODO=\"WAITING\"" ,cm/org-sort-style)
(todo "PROJECT" ,cm/org-sort-style)))
("i" "Inbox"
((tags-todo "-{.*}-TODO=\"PROJECT\"")))
("n" "Next agenda"
((todo "NEXT")
(todo "WAITING")
(todo "STARTED"))
,cm/org-sort-style)))
(defun cm/context-agendas ()
`(("p" "Project Agenda"
((todo "PROJECT")
(todo "WAITING"))
,cm/org-sort-style)
,(cm/org-ctx-agenda "v" "INTERNET" "Internet Agenda")
,(cm/org-ctx-agenda "c" "COMPUTER" "Computer Agenda")
,(cm/org-ctx-agenda "w" "WORK" "Work Agenda")
,(cm/org-ctx-agenda "h" "HOME" "Home Agenda")
,(cm/org-ctx-agenda "r" "ERRAND" "Errand Agenda")
,(cm/org-ctx-agenda "f" "PHONE" "Phone Agenda")
,(cm/org-ctx-agenda "l" "AGENDA" "People Agenda")
("ct" "Tickler Agenda"
((tags-todo "-TODO=\"PROJECT\"-BLOCKED=\"t\"-TODO=\"WAITING\"")
(todo "PROJECT"))
((org-agenda-skip-function '(org-agenda-skip-entry-if 'notscheduled))
(org-agenda-sorting-strategy '(tsia-up))))))
(defun cm/review-agendas ()
`(("rd" "Daily Review"
((agenda)
(todo "DONE|NVM"))
((org-agenda-span 3)
(org-agenda-start-day "-1d")))
("rs" "Weekly Snippet Review"
((agenda))
((org-agenda-span 7)
(org-agenda-start-on-weekday 1)
(org-agenda-start-with-log-mode t)
(org-agenda-archives-mode t)))
("rw" "Weekly Review Agenda"
(;; GET CLEAR
(tags-todo "noop" ,(cm/review-section "Get Clear!"))
(tags-todo "noop" ,(cm/review-header "Collect Loose Papers and Materials"))
(tags-todo "-{.*}-TODO=\"PROJECT\"" ,(cm/review-header "Get IN to ZERO"))
(tags-todo "noop" ,(cm/review-header "Empty Your Head!"))
;; GET CURRENT
(tags-todo "noop" ,(cm/review-section "Get Current!"))
(tags-todo "-TODO=\"PROJECT\"-BLOCKED=\"t\"-TODO=\"WAITING\""
(,@(cm/review-header "Review Actions Lists: Mark things that are invalid/done from the past week")
(org-agenda-skip-function '(org-agenda-skip-entry-if 'scheduled))))
(todo "DONE|NVM")
(tags-todo "-TODO=\"PROJECT\"-BLOCKED=\"t\"-TODO=\"WAITING\""
(,@(cm/review-header "Review Scheduled Actions Lists")
(org-agenda-skip-function '(org-agenda-skip-entry-if 'notscheduled))))
(agenda "" (,@(cm/review-header "Review Previous Week Calendar Data")
(org-agenda-skip-function nil)
(org-agenda-span 14)
(org-agenda-start-day ,(concat "-" (number-to-string (+ 14 (cm/days-back-to-monday))) "d"))))
(agenda "" (,@(cm/review-header "Review Current Week Calendar Data")
(org-agenda-skip-function nil)
(org-agenda-span 7)
(org-agenda-start-day ,(concat "-" (number-to-string (cm/days-back-to-monday)) "d"))))
(agenda "" (,@(cm/review-header "Review Next Week Calendar Data")
(org-agenda-skip-function nil)
(org-agenda-span 14)
(org-agenda-start-day ,(concat "+" (number-to-string (- 7 (cm/days-back-to-monday))) "d"))))
(todo "WAITING" ,(cm/review-header "Review Waiting For List: Mark things as done, add things you're waiting for"))
(todo "PROJECT" ,(cm/review-header "Review Projects and Larger Outcomes: Add missing next actions, make sure each is naturally planned"))
(tags "CHECKLIST" ,(cm/review-header "Review Relevant Checklists: Any missing? Any new ones that could be added?"))
;; GET CREATIVE
(tags-todo "noop" ,(cm/review-section "Get Creative!"))
(tags "SOMEDAY" ,(cm/review-header "Review Someday/Maybe List"))
(tags-todo "noop" ,(cm/review-header "Empty Your Head!")))
((org-agenda-span 22)
(org-agenda-start-on-weekday 1)
(org-agenda-start-day ,(concat "-" (number-to-string (cm/days-back-to-monday)) "d"))))
("rq" "Quarterly Review Agenda"
(;; GET CLEAR
(tags-todo "noop" ,(cm/review-section "Get Clear!"))
(tags-todo "noop" ,(cm/review-header "Empty Your Head!"))
;; GET CURRENT
(tags-todo "noop" ,(cm/review-section "Get Current!"))
(tags "PRIORITY+50K" ,(cm/review-header "Read and Review Life Purpose"))
(tags "PRIORITY+40K" ,(cm/review-header "Read and Review Visions"))
(tags "PRIORITY+30K" ,(cm/review-header "Read and Review Goals and Objectives"))
(tags "PRIORITY+20K" ,(cm/review-header "Read and Review Areas of Focus and Responsibility"))
(todo "PROJECT" ,(cm/review-header "Read and Review Projects and Alignment"))
;; GET CREATIVE
(tags-todo "noop" ,(cm/review-section "Get Creative!"))
(tags-todo "noop" ,(cm/review-header "What Went Well This Quarter, and Why?"))
(tags-todo "noop" ,(cm/review-header "What Went Poorly This Quarter, and Why?"))
(tags-todo "noop" ,(cm/review-header "Am I Headed the Right Direction?"))
(tags-todo "noop" ,(cm/review-header "Empty Your Head!")))
((org-agenda-entry-text-maxlines 1000)
(org-agenda-entry-text-mode 1)))))
(defun cm/checklist-agendas ()
'(("xx" "All Checklists"
((tags "CHECKLIST"))
((org-agenda-entry-text-maxlines 100)
(org-agenda-add-entry-text-maxlines 100)))
("xd" "Daily Checklists"
((tags "DAILY+CHECKLIST"))
((org-agenda-entry-text-maxlines 100)
(org-agenda-add-entry-text-maxlines 100)))))
(defun cm/org-set-agenda ()
(interactive)
(setq org-agenda-custom-commands
(append
(cm/day-agendas)
(cm/context-agendas)
(cm/review-agendas)
(cm/checklist-agendas)
'(("Z" "Receipt Agenda"
((org-receipt-agenda)))))))Dynamism! So a big part of what makes these nice is that they compute
what the right numbers of days on either side (via ndays) so I can see
weeks surrounding reviews. Unfortunately, these are the parts that
require the cm/org-load-agenda function as opposed to just being
able to set org-agenda-custom-commands
(defun cm/days-back-to-monday ()
(mod (+ (calendar-day-of-week (calendar-gregorian-from-absolute (org-today))) 6) 7))
(defun cm/days-to-next-saturday ()
(- 21 (cm/days-back-to-monday)))
(defun cm/org-load-agenda ()
(interactive)
(with-current-buffer
(if (equalp major-mode 'org-mode)
(current-buffer)
(get-buffer-create "*scratch*"))
(cm/org-set-agenda)
(org-agenda)))Also, set it the first time on startup so org commands work as expected.
(cm/org-set-agenda)print agendas
(setq org-agenda-exporter-settings
'((ps-print-color-p 'black-white)
(ps-landscape-mode t)
(htmlize-output-type 'css)))Agenda todo/done capture commands
This is really useful for Projects and others where I want to create sub-tasks / references quickly from the agenda view. Thanks yet again to Sacha Chua.
(require 'org-agenda)
(defun cm/org-agenda-done (&optional arg)
"Mark current TODO as done.
This changes the line at point, all other lines in the agenda referring to
the same tree node, and the headline of the tree node in the Org-mode file."
(interactive "P")
(org-agenda-todo "DONE"))
(define-key org-agenda-mode-map "x" #'cm/org-agenda-done)
(defun cm/org-agenda-mark-done-and-add-followup ()
"Mark the current TODO as done and add another task after it.
Creates it at the same level as the previous task, so it's better to use
this with to-do items than with projects or headings."
(interactive)
(org-agenda-todo "DONE")
(org-agenda-switch-to)
(org-capture 0 "t"))
(define-key org-agenda-mode-map "X" #'cm/org-agenda-mark-done-and-add-followup)
(defun cm/org-agenda-new ()
"Create a new note or task at the current agenda item.
Creates it at the same level as the previous task, so it's better to use
this with to-do items than with projects or headings."
(interactive)
(org-agenda-switch-to)
(org-capture 0))
(define-key org-agenda-mode-map "N" #'cm/org-agenda-new)
(defun cm/org-agenda-new-project-todo ()
"Create a new note or task at the current project's level."
(interactive)
(org-agenda-switch-to)
(if (not (equalp (nth 2 (org-heading-components)) "PROJECT"))
(error (format "%s %s" (org-heading-components)
": Not a project tagged element! Cannot add dependent action"))
(org-capture nil "b")))
(define-key org-agenda-mode-map "K" #'cm/org-agenda-new-project-todo)Org Clock
This adds automatic task clocking. It’s incredibly useful to figure out how long tasks took that you start and stop.(defun org-clock-in-if-starting ()
"Clock in when the task is marked STARTED."
(when (and (string= org-state "STARTED")
(not (string= org-last-state org-state)))
(org-clock-in)))
(defun org-clock-out-if-waiting ()
"Clock in when the task is marked STARTED."
(when (and (string= org-state "WAITING")
(not (string= org-last-state org-state))
(org-clocking-p))
(org-clock-out)))
(defun org-clock-out-if-started-todo ()
(when (and (or (string= org-state "TODO")
(string= org-state "NEXT"))
(string= org-last-state "STARTED")
(org-clocking-p))
(org-clock-out)))
(add-hook 'org-after-todo-state-change-hook 'org-clock-in-if-starting)
(add-hook 'org-after-todo-state-change-hook 'org-clock-out-if-waiting)
(add-hook 'org-after-todo-state-change-hook 'org-clock-out-if-started-todo) (defun cm/org-currently-scheduled ()
"Find what is currently scheduled for right now."
(interactive)
(message "Not implemented."))
(defun cm/org-currently-doing ()
"Find what is currently clocked as STARTED.
Looks over the `org-agenda-files' and finds all headings that are
tagged STARTED."
(interactive)
(setq files (org-agenda-files nil 'ifmode)
allitems nil)
(while (setq file (pop files))
(catch 'nextfile
(org-check-agenda-file file)
(setq rtn (org-agenda-get-day-entries file (calendar-gregorian-from-absolute (org-today)) :todo))
(setq allitems (append allitems rtn))))
(org-agenda-propertize-selected-todo-keywords "STARTED")
(org-agenda-finalize-entries allitems 'todo))Clocking hydra
This add some commands for managing and clocking my time in a hydra.
(defhydra hydra-org-clock (global-map "C-c x")
"Navigate your clocking"
("x" (lambda ()
(interactive)
(let ((current-prefix-arg 4))
(call-interactively #'org-clock-in)))))Useful setq’s
(setq org-stuck-projects
'("TODO=\"PROJECT\"" ("TODO" "NEXT" "STARTED") () ""))
(add-to-list 'auto-mode-alist '("\\.org'$" . org-mode))
(global-set-key (kbd "C-c l") 'org-store-link)
(cm/defun-local-wrap cm/org-load-agenda-wrapped cm/org-load-agenda)
(global-set-key (kbd "C-c a") 'cm/org-load-agenda-wrapped)
(setq org-fontify-done-headline t)
(setq org-clock-persist 'history)
(org-clock-persistence-insinuate)
(setq org-clock-idle-time 10)
(setq counsel-org-goto-display-style 'path)
(setq counsel-org-goto-separator " ➜ ")
(setq counsel-org-goto-face-style 'org)
(defun cm/counsel-org-goto (arg)
(interactive "P")
(if arg
(counsel-org-goto)
(counsel-org-goto-all)))
(define-key org-mode-map (kbd "C-c C-j") 'cm/counsel-org-goto)
(setq org-agenda-files '("~/org/"))
(setq org-refile-targets '((nil
:maxlevel . 5)
(("~/org/gtd.org"
"~/org/work.org")
:maxlevel . 5)))
(setq calendar-mark-diary-entries-flag t)
(setq org-adapt-indentation nil)
(setq org-agenda-dim-blocked-tasks t)
(setq org-agenda-include-all-todo t)
(setq org-agenda-include-diary t)
(setq org-agenda-inhibit-startup t)
(setq org-agenda-show-all-dates t)
(setq org-agenda-skip-deadline-if-done t)
(setq org-agenda-skip-scheduled-if-done t)
(setq org-agenda-span 'week)
(setq org-agenda-sticky t)
(setq org-agenda-tags-column -90)
(setq org-agenda-use-tag-inheritance '(search timeline agenda))
(setq org-attach-archive-delete 'query)
(setq org-attach-directory "~/org/_attachments/")
(setq org-enforce-todo-dependencies t)
(setq org-export-with-sub-superscripts nil)
(setq org-fontify-done-headline t)
(setq org-hide-leading-stars t)
(setq org-log-done t)
(setq org-outline-path-complete-in-steps nil) ;ido lol
(setq org-pretty-entities-include-sub-superscripts nil)
(setq org-refile-use-outline-path 'file)
(setq org-return-follows-link nil)
(setq org-reverse-note-order nil)
(setq org-special-ctrl-a/e t)
(setq org-special-ctrl-k t)
(setq org-start-on-weekday nil)
(setq org-tags-column -90)
(setq org-use-speed-commands t)
(setq org-use-sub-superscripts nil)
(setq org-use-tag-inheritance nil)
;(setq org-completion-use-ido t)
;; disable dumb-ass git crap for now
(advice-add 'org-attach-commit :override #'list)
;; less file local settings!
;; actually - let's do more file-local settings. Remove these asap.
(setq org-global-properties '(("Effort_ALL" . "0:10 0:20 0:30 0:40 0:50 1:00 2:00 3:00 4:00 5:00 6:00 7:00 8:00 9:00 10:00 11:00 12:00")))
(setq org-columns-default-format "%TODO %50ITEM(Task) %17Effort(Estimated Effort){:} %CLOCKSUM")
(setq org-imenu-depth 99)
(global-set-key (kbd "<f6>") 'org-clock-goto)
(setq org-structure-template-alist
(map 'list (lambda (x) (list (car x) (downcase (cadr x)))) org-structure-template-alist))Org Contacts
(use-package org-contacts
:init
(setq org-contacts-files '("~/org/_notes/contacts.org")))Archiving
These little functions do something super interesting for my GTD
workflow. When I go to archive a task, I generally want to move it
into these _archive folders I keep. However, if I’m working on a
long project, a lot of the time I like to keep notes or other
information that I’ve completed much closer to the project. So
whenever I press archive, I search to the parent heading using
outline-mode, and then get the tags from org. If the tags include
:PROJECT:, then I archive it to an * Archive subtree. If it’s
parent is not a project, then I assume it’s a bare todo, and archive
it the normal way.
(setq org-archive-location "_archive/%s_old::")
(defun cm/parent-tags ()
(interactive)
(save-excursion
(with-current-buffer (current-buffer)
(outline-show-all)
(outline-up-heading 1 t)
(nth 5 (org-heading-components)))))
(defun cm/dependent-project-todo-p ()
(interactive)
(save-excursion
(with-current-buffer (current-buffer)
(outline-show-all)
(let ((toret nil))
(dotimes (i (- (org-current-level) 1) toret)
(outline-up-heading 1 t)
(setq toret
(or toret
(string-equal "PROJECT"
(nth 2 (org-heading-components))))))))))
(defun cm/gtd-archiver ()
(interactive)
(if (cm/dependent-project-todo-p)
(org-archive-to-archive-sibling)
(org-archive-subtree)))
(setq org-archive-default-command #'cm/gtd-archiver)MobileOrg
(setq org-mobile-directory "~/org/.mobileorg-staging")
(add-hook 'org-mobile-post-push-hook
(lambda ()
(shell-command "pushorg")))
(add-hook 'org-mobile-pre-pull-hook
(lambda ()
(progn
(shell-command "cd $HOME/org/.mobileorg-staging && find . -type f -exec rm '{}' ';'")
(shell-command "pullorg"))))
(add-hook 'org-mobile-post-pull-hook
(lambda () (shell-command "cd $HOME/org/.mobileorg-staging && curl -n -T mobileorg.org https://dav.messagingengine.com/codemac.fastmail.com/files/org/mobileorg.org &")))
Sync my mobile-org files in a single function. This grabs everything
in “from-mobile.org” after an org-mobile-pull and puts it into
gtd.org/Inbox. At some point I’d love to call this from emacsclient,
but I don’t know what it would do if I was in the middle of refiling
or something else. For now, it’s manual.
(defun cm/get-inbox-pos ()
(save-excursion
(with-current-buffer "gtd.org"
(goto-char (point-min))
(re-search-forward "^\\* Inbox[ ]*$")
(move-beginning-of-line nil)
(point))))
(defun cm/org-sync ()
(interactive)
(save-excursion
(org-save-all-org-buffers)
;; skip the review agendas (which are slow)
(setq org-agenda-custom-commands
(append (cm/day-agendas)
(cm/context-agendas)
(cm/checklist-agendas)))
(org-mobile-pull)
(with-current-buffer "from-mobile.org"
(if (equalp (point-max) (point-min))
(message "Nothing to move in from-mobile.org, pushing current files.")
(goto-char (point-max))
(push-mark (point-min) t t)
(org-refile nil nil `("gtd.org/Inbox" ,(buffer-file-name (get-buffer "gtd.org")) "" ,(cm/get-inbox-pos)))
(org-save-all-org-buffers))
(org-mobile-push)
(org-save-all-org-buffers)))
;; set it back to normal
(cm/org-set-agenda)
(org-agenda nil "i")
(message "cm/org-sync completed"))
(global-set-key (kbd "C-c y") #'cm/org-sync)Tags and todo keywords
(setq org-tag-alist '((:startgroup)
("HOME" . ?h)
("WORK" . ?w)
("ERRAND" . ?r)
(:endgroup)
("AGENDA" . ?a)
("COMPUTER" . ?c)
("INTERNET" . ?v)
("PHONE" . ?f)
("EMAIL" . ?m)
("OUTREACH" . ?h)
("DISPATCH" . ?d)
("CHECKLIST" . ?x)))
(setq org-log-done '(note))
(setq org-log-into-drawer t) ; record state changes into LOGBOOK
(setq org-todo-keywords
'((sequence "TODO(t@)" "NEXT(n!)" "STARTED(s)" "WAITING(w@)" "PROJECT(p)" "|" "DONE(d@)" "NVM(v@)")))
(setq org-tags-exclude-from-inheritance '("CHECKLIST"))
;(setq org-agenda-category-icon-alist
; '(("netapp" "" nil t)org export
(require 'pandoc-mode)
;; mediawiki + pandoc hack
(setq pandoc-binary "/Users/jmickey/bin/pandoc")
(defun cm/org-export-as-pandoc ()
(interactive)
(org-export-as-html 3 nil "*Org HTML Export*" t nil)
(with-current-buffer "*Org HTML Export*"
(pandoc-mode 1)
(pandoc-set-write "mediawiki")
(pandoc-run-pandoc nil)
(pandoc-view-output)))
;(org-babel-load-file (expand-file-name
; "org-docco.org"
; (expand-file-name
; "scripts"
; (expand-file-name
; "contrib"
; (expand-file-name
; "org-src"
; dotfiles-dir)))))org babel & export
(if cm/using-rc
(setq org-babel-default-header-args:ditaa '((:results . "file")
(:exports . "results")
(:java . "'-Dfile.encoding=UTF-8'"))))
(setq cm/ditaa-paths
(list
(concat (getenv "HOME") "/bin/ditaa.jar")
"/usr/share/ditaa/ditaa.jar"
"/usr/share/java/ditaa/ditaa-0_9.jar"))
(setq org-ditaa-jar-path
(reduce
(lambda (x y) (if (and x (file-exists-p x)) x y))
cm/ditaa-paths))
(setq org-plantuml-jar-path plantuml-jar-path)
(add-hook 'org-babel-after-execute-hook 'org-display-inline-images)
(org-babel-do-load-languages
'org-babel-load-languages
'((emacs-lisp . t)
(C . t)
(R . t)
(awk . t)
(clojure . t)
(ditaa . t)
(dot . t)
(gnuplot . t)
(haskell . t)
(latex . t)
(ledger . t)
(lisp . t)
(org . t)
(perl . t)
(plantuml . t)
(python . t)
(ruby . t)
(scheme . t)
(shell . t)
(sql .t)))
(setq org-confirm-babel-evaluate t) ; nah, let's be safe
(autoload 'org-mime-org-buffer-htmlize "org-mime" "org-mime-org-buffer-htmlize" t nil)
(defun cm/org-mime-send-buffer-hook ()
(defun org-export-grab-title-from-buffer ()
""
"!! replace me !!"))
(add-hook 'org-mime-send-buffer-hook 'cm/org-mime-send-buffer-hook)Publishing
How I deploy my personal website.;; Inline images in HTML instead of producting links to the image
(setq org-export-html-inline-images t)
;; Use org.css from the norang website for export document stylesheets
;(setq org-export-html-style-extra "<link rel=\"stylesheet\" href=\"http://doc.norang.ca/org.css\" type=\"text/css\" />")
;(setq org-export-html-style-include-default nil)
;; Do not generate internal css formatting for HTML exports
(setq org-export-htmlize-output-type nil)
;; Export with LaTeX fragments
(setq org-export-with-LaTeX-fragments t)
;; List of projects
;; - codemac.net -- My personal website
;; - org-doc -- export all org documents
(setq org-publish-project-alist
'(("codemac-org"
:base-directory "~/org/_www/codemac.net"
:publishing-directory "/tmp/codemac.net"
:recursive t
:base-extension "org"
:publishing-function org-html-publish-to-html
:exclude-tags ("noexport" "todo")
:html-preamble ""
:html-postamble ""
:style nil
:section-numbers nil
:style-include-default nil
:auto-sitemap t
:sitemap-filename "sitemap.html")
("codemac-static"
:base-directory "~/org/_www/codemac.net"
:publishing-directory "/tmp/codemac.net"
:base-extension "css\\|pdf\\|png\\|jpg\\|gif\\|js\\|txt\\|mp3\\|ogg\\|swf"
:publishing-function org-publish-attachment
:recursive t)
("codemac" :components ("codemac-org" "codemac-static"))
("org-doc-org"
:base-directory "~/org/"
:publishing-directory "/tmp/published-org"
:recursive t
:section-numbers nil
:table-of-contents nil
:base-extension "org"
:publishing-function (org-html-publish-to-html org-org-publish-to-org)
:plain-source t
:htmlized-source t
:style-include-default nil
:style "<link rel=\"stylesheet\" href=\"/org.css\" type=\"text/css\" />"
:auto-sitemap t
:sitemap-filename "index.html"
:sitemap-title "Test Publishing Area"
:sitemap-style "tree"
:author-info nil
:creator-info nil)
("org-doc-static"
:base-directory "~/org/"
:publishing-directory "/tmp/published-org"
:base-extension "css\\|pdf\\|png\\|jpg\\|gif"
:publishing-function org-publish-attachment
:recursive t
:author nil)
("org-doc" :components ("org-doc-org" "org-doc-static"))))
(defun org-save-then-publish ()
(interactive)
(save-buffer)
(org-save-all-org-buffers)
(org-publish-current-project))Misc Funcs
(defun org-insert-datetime ()
(interactive)
(insert (format-time-string "%Y-%m-%d %a %H:%M %z")))
(defun cm/org-delete-link ()
"Replace an org link of the format [[LINK][DESCRIPTION]] with DESCRIPTION.
If the link is of the format [[LINK]], delete the whole org link.
In both the cases, save the LINK to the kill-ring.
Execute this command while the point is on or after the hyper-linked org link."
(interactive)
(when (derived-mode-p 'org-mode)
(let ((search-invisible t) start end)
(save-excursion
(when (re-search-backward "\\[\\[" nil :noerror)
(when (re-search-forward "\\[\\[\\(.*?\\)\\(\\]\\[.*?\\)*\\]\\]" nil :noerror)
(setq start (match-beginning 0))
(setq end (match-end 0))
(kill-new (match-string-no-properties 1)) ; Save the link to kill-ring
(replace-regexp "\\[\\[.*?\\(\\]\\[\\(.*?\\)\\)*\\]\\]" "\\2" nil start end)))))))Checklist Creation
This generates a set of TODOs based on a heading and a checklist. This allows me to create checklist templates per-project.
(defun cm/org-convert-checklist-to-todos (beg end)
"Converts a list in the format:
* Checklist Name :CHECKLIST:
- [ ] one
- [ ] two
Into a list that looks like:
* PROJECT Checklist name
** TODO one
** TODO two
With ID's regenerated and CREATED timestamps included."
(interactive (list
(save-excursion (org-back-to-heading t) (point))
(save-excursion (org-end-of-subtree t t) (point))))
(goto-char end)
(let ((template (buffer-substring beg end)))
;; Prepare clone.
(with-temp-buffer
(insert template)
(org-mode)
(goto-char (point-min))
(org-show-subtree)
(re-search-forward "^[[:space:]]*- ")
(move-beginning-of-line nil)
(push-mark (point-max) t t)
(org-toggle-heading)
(pop-mark)
;; Add "PROJECT" todo state to parent heading
(goto-char (point-min))
(org-todo "PROJECT")
(org-set-tags-to nil)
;; loop over children, adding ID and CREATED timestamps
(goto-char (point-max))
(while (outline-previous-heading)
(org-id-get-create t)
(org-set-property "CREATED" (with-temp-buffer
(org-mode)
(org-time-stamp-inactive '(16))
(buffer-string))))
(goto-char (point-max))
(insert "\n")
(mark-whole-buffer)
(let* ((inboxloc (org-id-find "492952f0-e1cf-41e8-a66d-cf1808f91619"))
(refileloc (list "Inbox" (car inboxloc) nil (cdr inboxloc))))
(org-refile nil nil refileloc nil)))))Link Types
;; org links!
(org-add-link-type "man" 'org-man-open)
(add-hook 'org-store-link-functions 'org-man-store-link)
(defcustom org-man-command 'man
"The Emacs command to be used to display a man page."
:group 'org-link
:type '(choice (const man) (const woman)))
(defun org-man-open (path)
"Visit the manpage on PATH.
PATH should be a topic that can be thrown at the man command."
(funcall org-man-command path))
(defun org-man-store-link ()
"Store a link to a manpage."
(when (memq major-mode '(Man-mode woman-mode))
;; This is a man page, we do make this link
(let* ((page (org-man-get-page-name))
(link (concat "man:" page))
(description (format "Manpage for %s" page)))
(org-store-link-props
:type "man"
:link link
:description description))))
(defun org-man-get-page-name ()
"Extract the page name from the buffer name."
;; This works for both `Man-mode' and `woman-mode'.
(if (string-match " \\(\\S-+\\)\\*" (buffer-name))
(match-string 1 (buffer-name))
(error "Cannot create link to this man page")))
;; cisco links
(defun org-link-type-cisco-open (path)
"path is the userid"
(shell-command (concat "open \"http://wwwin-tools.cisco.com/dir/details/" path "\"")))
(org-add-link-type "cisco" 'org-link-type-cisco-open)
(defun org-link-type-websec-open (path)
"path is the jira number"
(shell-command (concat "open \"https://jira.ironport.com/browse/WEBSEC-" path "\"")))
(org-add-link-type "websec" 'org-link-type-websec-open)
(defun org-link-type-sas-open (path)
"path is the jira number"
(shell-command (concat "open \"https://jira.ironport.com/browse/ENGSAS-" path "\"")))
(org-add-link-type "engsas" 'org-link-type-sas-open)
(defun org-link-type-netapp-open (path)
"path is username"
(shell-command (concat "open \"http://burtweb-prd.eng.netapp.com/burt/burt-bin/profile?user=" path "\"")))
(org-add-link-type "netapp" 'org-link-type-netapp-open)
;; capture for mac os x popup
(defun cm/org-capture-other-frame ()
"Create a new frame and run org-capture."
(interactive)
(make-frame '((name . "Org-Capture")
(width . 120)
(height . 20)
(menu-bar-lines . 0)
(tool-bar-lines . 0)
(auto-lower . nil)
(auto-raise . t)))
(select-frame-by-name "Org-Capture")
(if (condition-case nil
(progn (org-capture) t)
(error nil))
(delete-other-windows)
(cm/org-capture-other-frame-cleanup)))
(defun cm/org-capture-other-frame-hook (orig-fun &optional goto keys)
"Hook for org-capture to pop-up a frame instead of embed in emacs."
(interactive)
(make-frame '((name . "Org-Capture")
(width . 120)
(height . 40)
(menu-bar-lines . 0)
(tool-bar-lines . 0)
(auto-lower . nil)
(auto-raise . t)))
(select-frame-by-name "Org-Capture")
(if (condition-case nil
(progn (funcall orig-fun goto keys) t)
(error nil))
(delete-other-windows)
(cm/org-capture-other-frame-cleanup)))
(defun cm/org-capture-other-frame-cleanup ()
"Close the Org-Capture frame."
(if (equal "Org-Capture" (frame-parameter nil 'name))
(delete-frame)))
(advice-add 'org-capture :around #'cm/org-capture-other-frame-hook)
(add-hook 'org-capture-after-finalize-hook 'cm/org-capture-other-frame-cleanup)Search through my _notes directory
I like to record notes about various things in a long lived way via plain text. However, I haven’t actually used them that much because I forget what I’ve even recorded!
To help get rid of that, I want to create an emacs interface where I search through my notes based on headings, content, and possibly tags of some form as well.
Let’s first layout what we want the keymap to be like:
(defvar cm/org-notes-command-prefix)
(defvar cm/org-notes-command-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "s") 'cm/org-notes-search)
(define-key map (kbd "r") 'cm/org-notes-capture)
map))
(define-prefix-command 'cm/org-notes-command-prefix)
(fset 'cm/org-notes-command-prefix cm/org-notes-command-map)
(setq cm/org-notes-command-prefix cm/org-notes-command-map)
(global-set-key (kbd "C-c n") 'cm/org-notes-command-prefix)THIS WAS WAY OVER THE TOP. We can just use org-search-view.
(defun cm/org-notes-search ()
(interactive)
(let ((org-agenda-files (append org-agenda-files '("~/org/_notes"))))
(org-search-view)))
(defun cm/org-notes-capture ()
(interactive)
(org-capture nil "n"))Org Capture
;; org capture!
(defun cm/org-child-location ()
;; get id of current location
(pcase (org-id-find (org-id-get))
(`(,path . ,position)
(set-buffer (org-capture-target-buffer path))
(widen)
(org-capture-put-target-region-and-position)
(goto-char position))
(_ (error "Connot find target ID \"%s\"" id))))
(setq cm/humble-todo-template (list "* TODO %?\n:PROPERTIES:\n:CREATED: %U\n:END:\n%i\n%a\n\n" :prepend nil :empty-lines 1))
(setq org-directory "~/org")
(setq org-default-notes-file (concat org-directory "/gtd.org"))
(setq org-board-capture-file (concat org-directory "/_notes/www.org"))
(define-key global-map "\C-cr" 'org-capture)
(setq org-capture-templates
`(("t" "Todo" entry (file+headline "~/org/gtd.org" "Inbox")
,@cm/humble-todo-template)
("p" "Project" entry (id "a262e411-ff66-43fc-9124-341694564dda")
"* PROJECT %?\n:PROPERTIES:\n:CREATED:%U\n:END:\n\n** Actions\n\n** Archive :ARCHIVE:\n\n" :empty-lines 1)
("j" "Journal" entry (file "~/org/_editorial/journal.org")
"* %T %?\n\n %i\n %a\n\n" :prepend nil :empty-lines 1)
("c" "Add note to currently clocked task" entry (clock)
"* %U %?\n%a\n\n%i\n\n" :empty-lines 1)
("a" "Address Book" entry (file "~/org/_notes/contacts.org")
"* %(org-contacts-template-name)\n:PROPERTIES:\n:EMAIL: %(org-contacts-template-email)\n:END:" :empty-lines 1)
("n" "Timed Notes" entry (file "~/org/_notes/notes.org")
"* %T %?\n\n %i\n %a\n\n" :prepend nil :empty-lines 1)
("d" "Logged distraction entry" entry (file+headline "~/org/gtd.org" "Inbox")
,@cm/humble-todo-template
:clock-in t
:clock-resume t)
("w" "Work captures")
("wt" "Work Action" entry (file+headline "~/org/work.org" "Actions")
,@cm/humble-todo-template)
("wn" "Work Note" entry (file "~/org/work.org")
"* %U %?\n\n %i\n %a\n\n" :prepend nil :empty-lines 1)
("wj" "Work journal entry" plain (file+olp+datetree "~/org/work.org" "Lab Notes")
"\n\n%?\n\n" :tree-type week :empty-lines 1)
("ws" "Snippet Log" entry (file+headline "~/org/work.org" "Snippets")
"* S: %?\n %T\n\n" :prepend nil :empty-lines 1)
("r" "Review Captures")
("ry" "Review Entry For The Year" entry (id "256745aa-ece4-4649-a616-52b0194850d0")
"* Review for %<%Y>\n:PROPERTIES:\n:CREATED: %U\n:END:\n\n** Review previous year\n\n- What was completed?\n- What was not completed?\n- What was the best spent time?\n- What was the worst spent time?\n- What is the current financial, physical, emotional, mental, spiritual state?\n\n** Project future year\n\n- What to spend more time on in new year?\n- What to spend less time on in new year?\n- What projects should be started in the next year?\n- What project should be completed in the next year?\n\n" :empty-lines 1)
("b" "Agenda Project" entry (function cm/org-child-location)
,@cm/humble-todo-template)
("f" "Fitness" table-line (file+headline "~/org/fitness.org" "Fitness")
,(concat (format-time-string "| %Y.%m.%d-%H:%M |") " %^{Weight} | | %^{RHR} |") :table-line-pos "II-1" :empty-lines 1)
("xw" "org-capture reference" entry (file "~/org/_notes/www.org")
"* %U %?%:description\n:PROPERTIES:\n:URL: %:link\n:END:\n\n%:body\n\n" :empty-lines 1)
("xi" "org-capture inbox" entry (file+headline "~/org/gtd.org" "Inbox")
"* TODO %?%:annotation\n:PROPERTIES:\n:CREATED: %U\n:END:\n\n%:link\n\n%i\n\n" :empty-lines 1)))
(defun cm/org-board-dl-hook ()
(when (equal (buffer-name)
(concat "CAPTURE-" org-board-capture-file))
(org-board-archive)))
(add-hook 'org-capture-before-finalize-hook 'cm/org-board-dl-hook)
(defun gtd ()
(interactive)
(find-file "~/org/gtd.org"))
;; Set up my diary file
(setq diary-file "~/org/diary") ;; deal with the fact that it's in the org folder
;;
(setq org-src-fontify-natively nil)Also - remember to give everything an id number. This will really help any of my plans to get org mode todos into easier to consume formats.
(add-hook 'org-capture-prepare-finalize-hook 'org-id-get-create)Merge handling
When I open a buffer in SMerge mode, org has everything all folded up, and it becomes very difficult to see where things went. For ediff, diff, and smerge modes we should really not have any folding at all.
(add-hook 'ediff-prepare-buffer-hook #'outline-show-all)
(add-hook 'smerge-mode-hook #'outline-show-all)
(add-hook 'diff-mode-hook #'outline-show-all)Appt Reminders
This creates the appt buffer so we get notifications about appointments in the org mode calendar. It’s not perfect, but it does help with my forgetfulness. (setq appt-display-interval 2
appt-message-warning-time 6)
(appt-activate)
(org-agenda-to-appt)RCIRC
; (eval-after-load 'rcirc '(require 'rcirc-color))
; (require 'rcirc)
; ;; colors!
;
;
; (add-hook 'rcirc-markup-colors 'rcirc-markup-text-functions)
;
; (defvar rcirc-color-vector ["black" "red" "green" "yellow" "blue" "magenta" "cyan" "white"]
; "Vector of color names for the numbers 0-7.")
;
; (defun rcirc-markup-colors (process sender response channel-buffer)
; (while (re-search-forward "\C-c\\([0-7]\\)\\(.*?\\)\C-c" nil t)
; (rcirc-add-face (match-beginning 0) (match-end 0)
; (cons 'foreground-color
; (aref rcirc-color-vector (string-to-number (match-string 1)))))
; ;; start deleting at the end
; (delete-region (1- (match-end 0)) (match-end 0))
; (delete-region (match-beginning 0) (match-end 1))))
;
; ;; Turn on logging everything to a special buffer, for debugging.
; ;(setq rcirc-debug-flag t)
; ;; scroll as little as possible
; (add-hook 'rcirc-mode-hook
; (lambda ()
; (set
; (make-local-variable 'scroll-conservatively)
; 8192)))
;
; ;; add reconnect
; (eval-after-load 'rcirc
; '(defun-rcirc-command reconnect (arg)
; "Reconnect the server process."
; (interactive "i")
; (unless process
; (error "There's no process for this target"))
; (let* ((server (car (process-contact process)))
; (port (process-contact process :service))
; (nick (rcirc-nick process))
; channels query-buffers)
; (dolist (buf (buffer-list))
; (with-current-buffer buf
; (when (eq process (rcirc-buffer-process))
; (remove-hook 'change-major-mode-hook
; 'rcirc-change-major-mode-hook)
; (if (rcirc-channel-p rcirc-target)
; (setq channels (cons rcirc-target channels))
; (setq query-buffers (cons buf query-buffers))))))
; (delete-process process)
; (rcirc-connect server port nick
; rcirc-default-user-name
; rcirc-default-full-name
; channels))))
This means that the format of the join command no longer supports
space separated channels - however - does now support keyed/passworded
channels correctly. The format is: #channela,#channelb ,passwordb
; (eval-after-load 'rcirc
; '(defun-rcirc-command join (channels)
; "Join CHANNELS.
; CHANNELS is a comma- or space-separated string of channel names."
; (interactive "sJoin channels: ")
; (let* ((chanpass (split-string channels " " t))
; (split-channels (split-string (car chanpass) "," t))
; (buffers (mapcar (lambda (ch)
; (rcirc-get-buffer-create process ch))
; split-channels)))
; (rcirc-send-string process (concat "JOIN " channels))
; (when (not (eq (selected-window) (minibuffer-window)))
; (dolist (b buffers) ;; order the new channel buffers in the buffer list
; (switch-to-buffer b))))))
;
; (eval-after-load 'rcirc
; '(defun rcirc (arg)
; "Connect to all servers in `rcirc-server-alist'.
;
; Do not connect to a server if it is already connected.
;
; If ARG is non-nil, instead prompt for connection parameters."
; (interactive "P")
; (if arg
; (let* ((server (completing-read "IRC Server: "
; rcirc-server-alist
; nil nil
; (caar rcirc-server-alist)
; 'rcirc-server-name-history))
; (server-plist (cdr (assoc-string server rcirc-server-alist)))
; (port (read-string "IRC Port: "
; (number-to-string
; (or (plist-get server-plist :port)
; rcirc-default-port))
; 'rcirc-server-port-history))
; (nick (read-string "IRC Nick: "
; (or (plist-get server-plist :nick)
; rcirc-default-nick)
; 'rcirc-nick-name-history))
; (user-name (read-string "IRC Username: "
; (or (plist-get server-plist :user-name)
; rcirc-default-user-name)
; 'rcirc-user-name-history))
; (password (read-passwd "IRC Password: " nil
; (plist-get server-plist :password)))
; (channels (read-string "IRC Channels: "
; (plist-get server-plist :channels)))
; (encryption (rcirc-prompt-for-encryption server-plist)))
; (rcirc-connect server port nick user-name
; rcirc-default-full-name
; channels password encryption))
; ;; connect to servers in `rcirc-server-alist'
; (let (connected-servers)
; (dolist (c rcirc-server-alist)
; (let ((server (car c))
; (nick (or (plist-get (cdr c) :nick) rcirc-default-nick))
; (port (or (plist-get (cdr c) :port) rcirc-default-port))
; (user-name (or (plist-get (cdr c) :user-name)
; rcirc-default-user-name))
; (full-name (or (plist-get (cdr c) :full-name)
; rcirc-default-full-name))
; (channels (plist-get (cdr c) :channels))
; (password (plist-get (cdr c) :password))
; (encryption (plist-get (cdr c) :encryption))
; contact)
; (when server
; (let (connected)
; (dolist (p (rcirc-process-list))
; (when (string= server (process-name p))
; (setq connected p)))
; (if (not connected)
; (condition-case e
; (rcirc-connect server port nick user-name
; full-name channels password encryption)
; (quit (message "Quit connecting to %s" server)))
; (with-current-buffer (process-buffer connected)
; (setq contact (process-contact
; (get-buffer-process (current-buffer)) :host))
; (setq connected-servers
; (cons (if (stringp contact) contact server)
; connected-servers))))))))
; (when connected-servers
; (message "Already connected to %s"
; (if (cdr connected-servers)
; (concat (mapconcat 'identity (butlast connected-servers) ", ")
; ", and "
; (car (last connected-servers)))
; (car connected-servers))))))))
; (setq rcirc-default-nick "codemac")
; (setq rcirc-default-user-name "codemac")
; (setq rcirc-default-user-full-name "codemac")
;
; (setq rcirc-server-alist cm/rcirc-server-alist)
; (setq rcirc-authinfo cm/rcirc-authinfo)
; (setq rcirc-startup-channels-alist '(("\\.freenode\\.net$" "#emacs")))
;
; ; (setq rcirc-time-format "%H:%M ") ; << that format suckts, nvm.Reconnect
; (defun-rcirc-command reconnect (arg)
; "Reconnect the server process."
; (interactive "i")
; (if (buffer-live-p rcirc-server-buffer)
; (with-current-buffer rcirc-server-buffer
; (let ((reconnect-buffer (current-buffer))
; (server (or rcirc-server rcirc-default-server))
; (port (if (boundp 'rcirc-port) rcirc-port rcirc-default-port))
; (nick (or rcirc-nick rcirc-default-nick))
; channels)
; (dolist (buf (buffer-list))
; (with-current-buffer buf
; (when (equal reconnect-buffer rcirc-server-buffer)
; (remove-hook 'change-major-mode-hook
; 'rcirc-change-major-mode-hook)
; (let ((server-plist (cdr (assoc-string server rcirc-server-alist))))
; (when server-plist
; (setq channels (plist-get server-plist :channels)))))))
; (if process (delete-process process))
; (rcirc-connect server port nick rcirc-default-user-name rcirc-default-full-name channels)))))
;
; ;;; Attempt reconnection at increasing intervals when a connection is
; ;;; lost.
;
; (defvar rcirc-reconnect-attempts 0)
;
; ;;;###autoload
; (define-minor-mode rcirc-reconnect-mode
; nil nil " Reconnect" nil
; (if rcirc-reconnect-mode
; (progn
; (make-local-variable 'rcirc-reconnect-attempts)
; (add-hook 'rcirc-sentinel-hooks
; 'rcirc-reconnect-schedule nil t))
; (remove-hook 'rcirc-sentinel-hooks
; 'rcirc-reconnect-schedule t)))
;
; (defun rcirc-reconnect-schedule (process &optional sentinel seconds)
; (condition-case err
; (when (and (eq 'closed (process-status process))
; (buffer-live-p (process-buffer process)))
; (with-rcirc-process-buffer process
; (unless seconds
; (setq seconds (exp (1+ rcirc-reconnect-attempts))))
; (rcirc-print
; process "my-rcirc.el" "ERROR" rcirc-target
; (format "scheduling reconnection attempt in %s second(s)." seconds) t)
; (run-with-timer seconds nil 'rcirc-reconnect-perform-reconnect process)))
; (error (rcirc-print process "RCIRC" "ERROR" nil
; (format "%S" err) t))))
;
; (defun rcirc-reconnect-perform-reconnect (process)
; (when (and (eq 'closed (process-status process))
; (buffer-live-p (process-buffer process)))
; (with-rcirc-process-buffer process
; (when rcirc-reconnect-mode
; (if (get-buffer-process (process-buffer process))
; ;; user reconnected manually
; (setq rcirc-reconnect-attempts 0)
; (let ((msg (format "attempting reconnect to %s..."
; (process-name process))))
; (rcirc-print process "my-rcirc.el" "ERROR" rcirc-target
; msg t))
; ;; remove the prompt from buffers
; (condition-case err
; (progn
; (save-window-excursion
; (save-excursion
; (rcirc-cmd-reconnect nil)))
; (setq rcirc-reconnect-attempts 0))
; ((quit error)
; (incf rcirc-reconnect-attempts)
; (rcirc-print process "my-rcirc.el" "ERROR" rcirc-target
; (format "reconnection attempt failed: %s" err) t)
; (rcirc-reconnect-schedule process))))))))SLIME
(setq inferior-lisp-program "sbcl")
(require 'slime-autoloads)
(slime-setup '(slime-fancy))
;(load (expand-file-name "/home/codemac/.quicklisp/slime-helper.el"))
SQL
Who needs a command line anyways..(defun sql-make-smart-buffer-name ()
"Return a string that can be used to rename a SQLi buffer.
This is used to set `sql-alternate-buffer-name' within
`sql-interactive-mode'."
(or (and (boundp 'sql-name) sql-name)
(concat (if (not(string= "" sql-server))
(concat
(or (and (string-match "[0-9.]+" sql-server) sql-server)
(car (split-string sql-server "\\.")))
"/"))
sql-database)))
(add-hook 'sql-interactive-mode-hook
(lambda ()
(setq sql-alternate-buffer-name (sql-make-smart-buffer-name))
(sql-rename-buffer)))W3M
(autoload 'w3m-browse-url "w3m-load" "" t)notmuch
I’m not a fan that this isn’t a simpleuse-package thing, but the
notmuch emacs implementation generally has to match the notmuch
binary - and it’s easier to just use whatever my distro packages.
(unless (require 'notmuch nil t)
(message "Notmuch could not be loaded, should disable sending mail"))
(setq cm/mail-script "mail-sync")
(defun cm/notmuch-poll ()
(interactive)
(unless (equal (call-process cm/mail-script nil nil) 0)
(error "Notmuch: poll script `%s' failed!" notmuch-poll-script)))
(defun cm/notmuch-refresh-buffer-after-poll (proc state)
(let ((buffer (process-buffer proc)))
(with-current-buffer buffer
(with-current-buffer notmuch-buffer-to-refresh
(when notmuch-buffer-refresh-function
(call-interactively notmuch-buffer-refresh-function))))))
(defun cm/notmuch-poll-and-refresh-this-buffer ()
(interactive)
(let ((old-buff (current-buffer)))
(with-current-buffer (get-buffer-create "*notmuch-mail-sync*")
(set (make-local-variable 'notmuch-buffer-to-refresh) old-buff)
(let ((process (start-process-shell-command "async proc mail-sync" (current-buffer) "mail-sync")))
(set-process-sentinel process #'cm/notmuch-refresh-buffer-after-poll)))))
(advice-add 'notmuch-poll :override #'cm/notmuch-poll)
(advice-add 'notmuch-poll-and-refresh-this-buffer :override #'cm/notmuch-poll-and-refresh-this-buffer)Sending mail
We’re just borrowing the gnus settings here..; (setq smtpmail-auth-credentials "~/.netrc") ;; is this needed?The smtpmail-multi-accounts block has a strict ordering. By using ssl I get to avoid most of the complications. It goes, in order:
smtpmail-smtp-usersmtpmail-smtp-serverstmpmail-smtp-service(port)mail-specify-envelope-from(not needed, especially if you’re using this.smtpmail-stream-type(ssl, starttls, or plain. USE SSL if you want to live)- starttls crap
- starttls crap
smtpmail-local-domain(hostname to use as where it’s sent from. I set this usually to the hostname of the email provider.)
(setq smtpmail-multi-accounts '((google . ("jmickey@google.com"
"smtp.gmail.com"
465
nil
ssl
nil
nil
"google.com"))
(codemac . ("codemac@fastmail.com"
"mail.messagingengine.com"
465
nil
ssl
nil
nil
"codemac.net"))))
(setq smtpmail-multi-default-account 'codemac)
(setq smtpmail-multi-associations '(("jmickey@google.com" google)
("j@codemac.net\\|jeff@archlinux.org\\|j@mickey.email\\|jeff@mickey.email\\|jm@vt.edu\\|jmickey@vt.edu" codemac)))
(setq send-mail-function 'smtpmail-multi-send-it)
(setq message-send-mail-function 'smtpmail-multi-send-it)
queueing
(setq smtpmail-queue-mail t ;; start in non-queuing mode
smtpmail-queue-dir "~/mail/queue")
(setq user-full-name "Jeff Mickey")
(if (equalp (system-name) "jmickey-glaptop0")
(setq user-mail-address "jmickey@google.com")
(setq user-mail-address "j@codemac.net"))Drafts Handling
Ridiculous hacks to get sensible synced drafts. Sometimes I end up with way too many drafts saved due to mbsync being fast, but that’s a good problem to me :)
;; how to get these things to not suck
(setq message-draft-headers '(From References Subject Date (optional . In-Reply-To) Message-ID (optional . User-Agent)))
(setq message-generate-headers-first t) ;; would love to do this without Date
(setq message-auto-save-directory "~/mail/cm/Drafts/cur")
(defun cm/draft-insert-mail-header-separator ()
"Insert `mail-header-separator' in the first empty line of the message.
`message-mode' needs this line to know where the headers end and
the body starts. Note, in `mu4e-compose-mode', we use
`before-save-hook' and `after-save-hook' to ensure that this
separator is never written to the message file. Also see
`mu4e-remove-mail-header-separator'."
;; we set this here explicitly, since (as it has happened) a wrong
;; value for this (such as "") breaks address completion and other things
(set (make-local-variable 'mail-header-separator)
(purecopy "--text follows this line--"))
(put 'mail-header-separator 'permanent-local t)
(save-excursion
;; make sure there's not one already
(cm/draft-remove-mail-header-separator)
(let ((sepa (propertize mail-header-separator
'intangible t
;; don't make this read-only, message-mode
;; seems to require it being writable in some cases
;;'read-only "Can't touch this"
'rear-nonsticky t
'font-lock-face 'message-separator)))
(widen)
;; search for the first empty line
(goto-char (point-min))
(if (search-forward-regexp "^$" nil t)
(replace-match sepa)
(progn ;; no empty line? then prepend one
(goto-char (point-max))
(insert "\n" sepa))))))
(defun cm/draft-remove-mail-header-separator ()
"Remove `mail-header-separator; we do this before saving a
file (and restore it afterwards), to ensure that the separator
never hits the disk. Also see `mu4e-draft-insert-mail-header-separator."
(interactive)
(save-excursion
(widen)
(goto-char (point-min))
;; remove the --text follows this line-- separator
(when (search-forward-regexp (concat "^" mail-header-separator) nil t)
(let ((inhibit-read-only t))
(replace-match "")))))
(defun cm/notmuch-message-setup ()
"Configures a bunch of hooks for notmuch message windows"
(message-add-action `(message "debug: done exit actions") 'exit)
(message-add-action `(message "debug: done postpone actions") 'postpone)
(message-add-action `(message "debug: done kill actions") 'kill)
(message-add-action 'notmuch-message-postpone-keep 'postpone)
(message-add-action 'notmuch-message-postpone-cleanup 'exit))
(add-hook 'message-mode-hook 'cm/notmuch-message-setup)
(add-hook 'message-mode-hook
(lambda ()
(add-hook 'after-save-hook 'cm/draft-insert-mail-header-separator nil 'make-it-local)
(add-hook 'before-save-hook 'cm/draft-remove-mail-header-separator nil 'make-it-local)))
(defun notmuch-message-postpone-cleanup ()
"Remove autosave and postponed messages for that buffer"
(message "debug: postpone cleanup hook")
(message "deleting draft file: %s" notmuch-draft-filename)
(if (file-exists-p notmuch-draft-filename)
(progn
(kill-buffer)
(delete-file notmuch-draft-filename)
(if (file-exists-p notmuch-draft-filename)
(message "failed to delete file %s" notmuch-draft-filename)
(message "debug: file deleted"))
)
(message "draft file %s doesn't exist" notmuch-draft-filename)))
(defun notmuch-message-postpone-keep ()
"Moves the previous buffer into the postponed folder and then kill it"
(save-excursion
(set-buffer (last-buffer))
(cm/draft-remove-mail-header-separator)
(notmuch-maildir-fcc-write-buffer-to-maildir "~/mail/cm/drafts" t)
(kill-buffer)))
(defun notmuch-show-resume-message ()
"Resume a postponed message."
(interactive)
(setq tmpfilename (notmuch-show-get-filename))
(notmuch-show-view-raw-message)
(setq buffer-file-name tmpfilename)
(message "debug: set buffer file name to %s" buffer-file-name)
(setq notmuch-draft-filename buffer-file-name)
(make-local-variable 'notmuch-draft-filename)
(message "debug: set draft file name to %s" notmuch-draft-filename)
(message-mode)
(cm/draft-insert-mail-header-separator))
;; set these to something damn high I hate elided messages
(setq notmuch-wash-citation-lines-prefix 1024)
(setq notmuch-wash-citation-lines-suffix 1024)
(setq notmuch-wash-signature-lines-max 0)
(setq notmuch-wash-wrap-lines-length (if (< fill-column 100) 100 fill-column))
(setq notmuch-maildir-use-notmuch-insert nil)
(require 'notmuch-show)
(define-key notmuch-show-mode-map "e" 'notmuch-show-resume-message)Keybindings
For some reason the= sign is used for refresh in notmuch. This is
horrifying.
(setq notmuch-archive-tags '("-inbox" "-unread" "+archive"))
(defun cm/notmuch-archive-unread-all ()
(interactive)
(notmuch-search-tag-all notmuch-archive-tags))
(defvar cm/private-notmuch-saved-searches '()
"Saved searches not to be published publicly")
(setq notmuch-saved-searches
'((:name "inbox" :query "tag:inbox" :key "i" :sort-order oldest-first)
(:name "unread" :query "tag:unread" :key "u" :sort-order newest-first)
(:name "work-inbox" :query "tag:work AND tag:inbox" :key "wi" :sort-order oldest-first)
(:name "work-bulk" :query "tag:work AND tag:unread"
:key "wb" :sort-order newest-first)
(:name "inbox-codemac" :query "tag:codemac AND tag:inbox" :key "c" :sort-order newest-first)
(:name "drafts" :query "tag:draft" :key "d" :sort-order newest-first)
(:name "today" :query "date:today..!" :key "t" :sort-order oldest-first)
(:name "sent" :query "tag:sent" :key "n")))
(cm/defun-local-wrap cm/notmuch-jump-search notmuch-jump-search)
(global-set-key (kbd "C-c i") #'cm/notmuch-jump-search)
;; keep the "goto address" logic consistent with org mode
(define-key notmuch-show-mode-map (kbd "C-c C-o") 'goto-address-at-point)
(setq notmuch-tagging-keys
`((,(kbd "a") notmuch-archive-tags "Archive")
(,(kbd "u") notmuch-show-mark-read-tags "Mark read")
(,(kbd "f")
("+flagged")
"Flag")
(,(kbd "m")
("+muted")
"Mute")
(,(kbd "s")
("+spam" "-inbox")
"Mark as spam")
(,(kbd "d")
("+deleted" "-inbox")
"Delete")))
(cm/defun-local cm/read-mail-command-notmuch ()
(notmuch-search "tag:inbox" t))
(setq read-mail-command #'cm/read-mail-command-notmuch)What is this sillyness? g should always refresh a buffer, shit man.
(eval-after-load 'notmuch
'(define-key notmuch-common-keymap "g" 'notmuch-refresh-this-buffer))
(eval-after-load 'notmuch
'(define-key notmuch-common-keymap "A" 'cm/notmuch-archive-unread-all))Notmuch fcc dir handling
(setq notmuch-fcc-dirs '((".*@google.com" . "work/sent")
(".*@codemac.net" . "cm/sent")))Message view
The vast majority of this is around setting up drafts handling in notmuch to be sync’d over IMAP.notmuch doesn’t handle this for us,
and this is an amalgamation of things I found online. It currently
works with the cyrus IMAP server.
(setq message-citation-line-format "* %f [%Y-%m-%d %H:%M]:")
(setq message-citation-line-function 'message-insert-formatted-citation-line)Set background color of eww rendered buffers to make wayyy more sense.
(setq shr-color-visible-luminance-min 80)Set the colors of each search result line based on the tags of the result. This was inspired because notmuch changed them to have insane defaults.
(setq notmuch-search-line-faces `(("unread" . (:weight bold))
("flagged" . (:foreground "khaki"))))(require 'messages-are-flowing)
(defun cm/message-mode-hook ()
(local-set-key (kbd "C-c M-o") 'org-mime-htmlize)
;; orgstruct mode conflicts with hard-newlines. format=flowed is
;; *more* important than org-mime-htmlize.
;; (orgstruct-mode 1)
(turn-off-auto-fill)
(setq truncate-lines nil
word-wrap t
use-hard-newlines t)
(visual-line-mode t)
(messages-are-flowing-use-and-mark-hard-newlines))
(add-hook 'message-mode-hook 'cm/message-mode-hook)Indentations get really silly in corporate mail. I wish there was an easier way to visually see “this is a reply” without indenting into the future.
(setq notmuch-show-indent-messages-width 0)Custom
Guix
;; load up guix if guix is installed!
(require 'guix-init nil t)Journal
Old text file journaling stuff. It really was quite nice, but org mode once again ate my soul.(defun insert-date ()
(interactive)
(insert (format-time-string "%c")))
(defun insert-header-newday ()
(interactive)
(insert "\n////////////////////////////////////////////////////////////////////////\n")
(insert "// ")
(insert-date)
(insert "\n\n"))
(defun insert-header-continue ()
(interactive)
(insert (format-time-string "\n ** %T **"))
(insert "\n\n"))
(defun insert-correct-header ()
(interactive)
(insert-header-newday))
(defun journal ()
(interactive)
(find-file "~/doc/journal.txt")
(end-of-buffer)
(insert-correct-header)
(auto-fill-mode 1)
(flyspell-mode 1))Blog
Some helper functions for publishing with ikiwiki(defun blog-insert-meta ()
(interactive)
(insert "[[!meta title=\"\"]]\n")
(insert "[[!tag ]]\n")
(insert "\n"))
(defun blog-last ()
(interactive)
(let ((wiki-dir "~/www/wiki/blog/"))
(find-file
(concat wiki-dir
(number-to-string (apply 'max (mapcar 'string-to-number
(mapcar '(lambda (a) (substring a 0 -5))
(directory-files wiki-dir nil "[0-9]*\\.mdwn" t )))))
".mdwn"))))
(defun blog-find-next ()
(interactive)
(let ((wiki-dir "~/www/wiki/blog/"))
(find-file
(concat wiki-dir
(number-to-string (1+ (apply 'max
(mapcar 'string-to-number
(mapcar '(lambda (a) (substring a 0 -5))
(directory-files wiki-dir nil "[0-9]*\\.mdwn" t))))))
".mdwn"))))
(defun blog-next ()
(interactive)
(blog-find-next)
(end-of-buffer)
(blog-insert-meta)) IronPort
A wholly owned subsidiary of Cisco.(defun ip-p4-cmd (command)
"Run a command through p4 correctly, synchronously."
(interactive)
(let ((bn (buffer-file-name))
(ppos (point)))
(call-process-shell-command
(concat
"P4USER=jmickey "
"P4PORT=perforce.ironport.com:1666 "
"P4CONFIG=P4ENV "
command " "
bn))
(find-alternate-file bn)
(goto-char ppos)))
(defun ip-p4-info (cmd)
"Run a command through p4 asynchronously in an output buffer"
(interactive)
(let* ((bfn (buffer-file-name))
(nbn (concat "*p4i:" (buffer-name) "*")))
(start-process-shell-command nbn
(get-buffer-create nbn)
(concat
"P4USER=jmickey "
"P4PORT=perforce.ironport.com:1666 "
"P4CONFIG=P4ENV "
cmd " "
bfn))
(switch-to-buffer nbn)))
(defun ip-p4-edit ()
"Mark file as edit in perforce, reload buffer as editable, reset pointer"
(interactive)
(ip-p4-cmd "p4 edit"))
(defun ip-p4 ()
"Run arbitrary p4 command on current file"
(interactive)
(ip-p4-cmd (concat "p4 " (ido-completing-read "p4 "
(list
"edit"
"revert")))))
(defun ip-p4-filelog ()
"Show filelog output"
(interactive)
(ip-p4-info "p4 filelog -i"))
(defun ip-p4pr ()
"Show perforce blame"
(interactive)
(ip-p4-info "p4pr"))
(defun sql-connect-preset (name)
"Connect to a predefined SQL connection listed in `sql-connection-alist'"
(eval `(let ,(cdr (assoc name ip-sql-connection-alist))
(flet ((sql-get-login (&rest what)))
(sql-product-interactive sql-product)))))
(defun ip-sql-get-names (tlist)
(if tlist (append (list (caar tlist)) (ip-sql-get-names (cdr tlist)))))
(defun ip-sql-connect ()
"Ido ask which!"
(interactive)
(sql-connect-preset (ido-completing-read "Connect to: " (ip-sql-get-names ip-sql-connection-alist))))Tup
(require 'tup-mode)Tools
Time Saved Calculater
The cm/timesaved function is a user interface for calculating how
much time someone saves if they shave off some amount of time per
occurrence of a task. Good example: morning routines, imagine if you
shaved off 10 seconds, but you know you’re going to do that every day
for the rest of your life, those 10 seconds add up! In fact, you save
2 days 12 hours and 50 minutes.
The idea for this comes from https://xkcd.com/1205/
(defun cm/timesaved-unit-message (totalremaining unit)
(let ((total (floor (/ totalremaining (cm/timesaved-seconds-in-time unit)))))
(format
"%s %s "
total
(if (> total 1) (concat unit "s") unit))))
(defun cm/timesaved-subtract-unit (totalremaining unit)
(mod totalremaining (cm/timesaved-seconds-in-time unit)))
(defun cm/timesaved-seconds-to-breakdown (seconds)
(let ((totalremaining seconds)
(messagestring ""))
(mapc (lambda (unit)
(when (> totalremaining (cm/timesaved-seconds-in-time unit))
(setq messagestring (concat messagestring (cm/timesaved-unit-message totalremaining unit)))
(setq totalremaining (cm/timesaved-subtract-unit totalremaining unit))))
(list "year" "month" "week" "day" "hour" "minute" "second"))
(message messagestring)))
(defun cm/timesaved-times-per-year (time)
(/ (cm/seconds-in-time "year")
(cm/seconds-in-time time)))
(defun cm/timesaved-seconds-in-time (time)
(message (format "intern time: %S" (intern time)))
(pcase (intern time)
('second 1)
('minute 60)
('hour (* 60 60))
('day (* 60 60 24))
('week (* 60 60 24 7))
('month (* 60 60 24 30))
('year (* 60 60 24 365))
(_ (error "No proper time string"))))
;; Notations:
;;
;; would be awesome to think of a much shorter notation
;; <number meaning x times><character meaning for y units> <number of years>
;; my lowest unit is a second, so we convert everything to seconds
(defun cm/timesaved (occurrenceunit occurrences years savedunit savedperoccurrence)
(interactive
(list
(completing-read
"Multiple occurrences per what unit? "
'("second" "minute" "hour" "day" "week" "month" "year"))
(string-to-number (read-string "How many occurances per unit? "))
(string-to-number (read-string "How many years will this event repeat for? "))
(completing-read
"What unit of measure for how much is saved? "
'("second" "minute" "hour" "day" "week" "month" "year"))
(string-to-number (read-string "How much of the unit is saved per occurance? "))))
(message (format "You will save: %s"
(cm/timesaved-seconds-to-breakdown
(* (cm/timesaved-times-per-year occurrenceunit) occurrences years
savedperoccurrence (cm/timesaved-seconds-in-time savedunit))))))
Capture all open buffers
The david allen special inbox magic
(defvar cm/obvious-buffers
'("*Help*" "*scratch*" "*GNU Emacs*" "*Messages*" "diary" "bbdb")
"Buffers that are obviously not relevant")
(defun cm/filter-out-obvious (ls)
(require 'seq)
(let ((obv-buffs (append cm/obvious-buffers
(org-agenda-files))))
(seq-remove (lambda (x) (or (string-prefix-p " " (buffer-name x))
(and (string-prefix-p "*" (buffer-name x))
(string-suffix-p "*" (buffer-name x)))
(member (buffer-name x) obv-buffs)
(member (buffer-file-name x) obv-buffs)))
ls)))
(defun cm/capture-buffer-links ()
(delete
nil
(cl-mapcar (lambda (buffer)
(with-current-buffer buffer
(if (and buffer-file-name
(y-or-n-p (format "store link from %s and close? " buffer-file-name)))
(progn
(let ((toret (org-store-link nil)))
(kill-buffer (current-buffer))
toret)))))
(cm/filter-out-obvious (buffer-list)))))
(defun cm/chrome-debug-tabs ()
(list
(delete
nil
(mapcar
(lambda (x)
(if (equal (cdr (assq 'type x)) "page")
(cons (cdr (assq 'title x)) (cdr (assq 'url x)))))
(with-current-buffer
(url-retrieve-synchronously "http://127.0.0.1:9222/json")
(replace-regexp "HTTP/1.1 200 OK\nContent-Length:.*\n.*UTF-8\n\n" "" nil (point-min) (point-max))
(beginning-of-buffer)
(json-read))))))
(defun cm/firefox-profile ()
(concat (car (directory-files (concat (getenv "HOME") "/.mozilla/firefox") t ".*default" t))
"/sessionstore-backups/recovery.js"))
(defun cm/firefox-tabs ()
"Retrieves the list of tabs from the current firefox session,
without any pinned tabs"
(mapcar
(lambda (x)
(delete
nil
(mapcar
(lambda (y)
(let* ((entries (cdr (assq 'entries y)))
(pinned (not (null (assq 'pinned y))))
(lastentry (aref entries (- (length entries) 1)))
(url (cdr (assq 'url lastentry)))
(title (cdr (assq 'title lastentry))))
(if (not pinned)
(cons title url))))
(cdr (assq 'tabs x)))))
(cdr (assq 'windows (json-read-file (cm/firefox-profile))))))
(defun cm/get-browser-tabs ()
(interactive)
"Makes a single list of all tabs open in all browsers"
(let ((tabs (apply #'append (append (cm/firefox-tabs) (cm/chrome-debug-tabs)))))
(delete nil
(cl-mapcar (lambda (title-url)
(if (and title-url (y-or-n-p (format "store %s - %s ? " (car title-url) (cdr title-url))))
(org-make-link-string (cdr title-url) (car title-url))))
tabs))))
(defun cm/close-n-capture ()
(interactive)
(org-capture-string (mapconcat #'identity
(append
(cm/capture-buffer-links)
(cm/get-browser-tabs))
"\n")
"t"))
;; I never use this
;;(global-set-key (kbd "C-c x") 'cm/close-n-capture)Global Abbrev Support
So something that cracks me up, is the concept of textexpander being so “powerful”. It’s not powerful - it’s just global.
Let’s use completing read for fuzzy matching on our abbrev. In emacs we can do it after typing the abbrev, I’ll need to figure out how to accomplish that across x11 apps though.
(defvar cm/emacs-expander-alist
"alist of things to expand to"
'(("//" . (text " // mickey"))))
(setq cm/emacs-expander-alist
'(("//" . (text " // mickey"))
("sg." . (text "Sounds good."))))
(defun cm/xdotool-key (key)
(make-process
:name "xdotool key process"
:buffer "xdotool-process-buffer"
:command `("xdotool" "key" "--clearmodifiers" "--delay" "5" ,key)
:connection-type 'pipe
:sentinel #'cm/expander-sentinel))
(defun cm/xdotool-type (text)
(let ((proc (make-process
:name "xdotool type process"
:buffer "xdotool-process-buffer"
:command '("xdotool" "type" "--clearmodifiers" "--delay" "5" "--file" "-")
:connection-type 'pipe
:sentinel #'cm/expander-sentinel)))
(process-send-string proc text)
(process-send-eof proc)))
(defvar cm/expander-temp-storage-reply
"So the callback can reply!" '())
(defun cm/expander-sentinel (proc event)
(cm/run-expander-plist cm/expander-temp-storage-reply))
(defun cm/run-expander-plist (pls)
(when (> (length pls) 1)
(let ((sym (car pls))
(val (cadr pls)))
(setq cm/expander-temp-storage-reply (cddr pls))
(pcase sym
('text (cm/xdotool-type val))
('key (cm/xdotool-key val))))))
(defun cm/emacs-expander ()
(interactive)
(save-window-excursion
(let* ((key (completing-read "Abbrev to expand: " cm/emacs-expander-alist))
(kpl (cdr (assoc key cm/emacs-expander-alist))))
(cm/emacs-expander-frame-cleanup)
(cm/run-expander-plist kpl))))
(defun cm/emacs-expander-frame ()
"Create a new frame and run cm/emacs-expander."
(interactive)
(save-window-excursion
(make-frame '((name . "emacs-expander")
(width . 120)
(height . 20)
(menu-bar-lines . 0)
(tool-bar-lines . 0)
(minibuffer . only)
(auto-lower . nil)
(auto-raise . t)))
(select-frame-by-name "emacs-expander")
(condition-case nil
(progn (cm/emacs-expander) t)
((error debug quit) nil)))
(cm/emacs-expander-frame-cleanup))
(defun cm/emacs-expander-frame-cleanup ()
"Close the emacs-expander frame."
(dolist (elem (frame-list))
(if (equalp "emacs-expander" (frame-parameter elem 'name))
(save-window-excursion
(delete-frame elem)))))Directory Specific Code
Directory classes
;; check in all the time! (Need to learn to avoid master as well..)
(defun cm/after-save-commit ()
(if cm/gitty-files
(call-interactively
(lambda (title)
(interactive "sCommit title: ")
(let* ((bfn (buffer-file-name))
(gitroot (substring (shell-command-to-string "git rev-parse --show-toplevel") 0 -1))
(shortname (car (split-string bfn (concat gitroot "/") t))))
(shell-command (concat "git add " bfn " && git commit -m '" shortname ": " title "'")))))))
;; set this variable in your .dir-locals.el so saving becomes enabled
(defvar cm/gitty-files nil)
(defun cm/igneous-product-config ()
;; add an after-save-hook that runs git commit!
(add-hook 'after-save-hook 'cm/after-save-commit))Directories
Disabled lisp
; have to figure out how to comment multiple lines...Desktop
Saves everything! Very useful for the epic emacs restarters (me);; (require 'desktop)
;;
;; (desktop-save-mode 1)
;;
;; ;; auto-save emacs instance
;; (defun cm/desktop-save ()
;; (interactive)
;; (if (eq (desktop-owner) (emacs-pid))
;; (desktop-save desktop-dirname)))
;;
;; (add-hook 'auto-save-hook 'cm/desktop-save)All edit in occur
Turns out that occur actually handles this just fine..;; (require 'all)
;; (defun isearch-all ()
;; "Invoke `all' from within isearch."
;; (interactive)
;; (let ((case-fold-search isearch-case-fold-search))
;; (all (if isearch-regexp isearch-string (regexp-quote isearch-string)))))
;; (define-key isearch-mode-map (kbd "C-e") 'isearch-all)Hippe tab
I.. don’t use this anymore;(global-set-key (kbd "TAB") 'hippie-expand)ECB - Emacs Code Browser
;(require 'ecb-autoloads)xcscope
Gotta love cscope. Using GNU Global now.;(require 'xcscope)
;(setq cscope-do-not-update-database t)
;
;(defun xcscope-minor-mode ()
; (interactive)
; (cscope:hook)
;)
;
;(add-hook 'python-mode-hook (function cscope:hook))acscope
Another way of handling cscope, though I rarely built cscope db’s locally, so this was rarely used.;(require 'ascope)
;
;;; The following line corresponds to be beginning of the "Cscope" menu.
;(define-key cscope:map "\C-css" 'ascope-find-this-symbol)
;(define-key cscope:map "\C-csg" 'ascope-find-global-definition)
;;(define-key cscope:map "\C-csG" 'cscope-find-global-definition-no-prompting)
;(define-key cscope:map "\C-csc" 'ascope-find-functions-calling-this-function)
;(define-key cscope:map "\C-csC" 'ascope-find-called-functions)
;(define-key cscope:map "\C-cst" 'ascope-find-this-text-string)
;;(define-key cscope:map "\C-cse" 'cscope-find-egrep-pattern)
;;(define-key cscope:map "\C-csf" 'cscope-find-this-file)
;(define-key cscope:map "\C-csi" 'ascope-find-files-including-file)
;(define-key cscope:map "\C-csa" 'ascope-all-symbol-assignments)
;;; --- (The '---' indicates that this line corresponds to a menu separator.)
;;(define-key cscope:map "\C-csb" 'cscope-display-buffer)
;;(define-key cscope:map "\C-csB" 'cscope-display-buffer-toggle)
;;(define-key cscope:map "\C-csn" 'cscope-next-symbol)
;;(define-key cscope:map "\C-csN" 'cscope-next-file)
;;(define-key cscope:map "\C-csp" 'cscope-prev-symbol)
;;(define-key cscope:map "\C-csP" 'cscope-prev-file)
;;(define-key cscope:map "\C-csu" 'cscope-pop-mark)
;;; ---
;;(define-key cscope:map "\C-csa" 'cscope-set-initial-directory)
;;(define-key cscope:map "\C-csA" 'cscope-unset-initial-directory)
;;; ---
;;(define-key cscope:map "\C-csL" 'cscope-create-list-of-files-to-index)
;;(define-key cscope:map "\C-csI" 'cscope-index-files)
;;(define-key cscope:map "\C-csE" 'cscope-edit-list-of-files-to-index)
;;(define-key cscope:map "\C-csW" 'cscope-tell-user-about-directory)
;;(define-key cscope:map "\C-csS" 'cscope-tell-user-about-directory)
;;(define-key cscope:map "\C-csT" 'cscope-tell-user-about-directory)
;;(define-key cscope:map "\C-csD" 'cscope-dired-directory))Perspective
;(require 'perspective)
;(persp-mode)Tabbar
Nice to have going along the top sometimes. Disabled, I never used it.;(require 'tabbar)
;(tabbar-mode)
;
;(global-set-key (kbd "<C-tab>") 'tabbar-forward)
;(global-set-key (kbd "<C-S-iso-lefttab>") 'tabbar-forward-group)End diasbled code
; this is where the ending mark would beCommand Frequency
This can be useful to figure out what commands you do and don’t use all the time, so you can figure out what keybindings you need to fix. I found that I never used the data though..;; (require 'command-frequency)
;; (setq-default command-frequency-table-file "~/.emacs-frequency")
;; (command-frequency-table-load)
;; (command-frequency-mode 1)
;; (command-frequency-autosave-mode 1)Ido
Everyone should use Ido. (well, maybe helm);(require 'ido)
;(put 'ido-exit-minibuffer 'disabled nil)
;(ido-mode t)If the exact name isn’t found, then flex matching will match against anything with the characters in the order you’ve typed. It matches only for strings that have that sequence of characters in order.
;(setq ido-enable-flex-matching t)Identica
Microblog, baby.;(autoload 'identica-mode "identica-mode" "" t)
;(setq identica-username cm/identica-username
; identica-password cm/identica-password)
;
;(global-set-key "\C-cip" 'identica-update-status-interactive)
;(global-set-key "\C-cid" 'identica-direct-message-interactive)Helm
Helm is the new Anything. Helm will help steer you in the right direction!Configure helm to be used for M-x… well let’s just use it for
everything. Helm is GNU for emacs. Helm is borg. We are the helm. You
will be assimilated.
Mostly lifted from https://tuhdo.github.io/helm-intro.html (note that this page is STUPID slow in eww)
;; (use-package helm
;; :diminish helm-mode
;; :bind (("M-x" . helm-M-x)
;; ("M-y" . helm-show-kill-ring)
;; ("C-x b" . helm-mini)
;; ("C-x C-f" . helm-find-files)
;; ("C-h SPC" . helm-all-mark-rings))
;; :init
;; (require 'helm-config)
;; (global-set-key (kbd "C-c h") 'helm-command-prefix)
;; (when cm/using-rc
;; (setq helm-top-command "COLUNMS=%s top -b -n 1"))
;; (helm-mode 1)
;; :config
;; (setq helm-split-window-in-side-p t
;; helm-ff-file-name-history-use-recentf t
;; helm-ff-search-library-in-sexp t
;; helm-move-to-line-cycle-in-source t
;; helm-scroll-amount 8
;; helm-ff-file-name-history-use-recentf t
;; helm-man-or-woman-function 'woman)
;; (when (executable-find "curl")
;; (setq helm-google-suggest-use-curl-p t))
;; (delete helm-source-locate helm-for-files-preferred-list)
;; ;; bindings I haven't figured out with :bind
;; (global-unset-key (kbd "C-x c"))
;; (define-key helm-map (kbd "<tab>") 'helm-execute-persistent-action)
;; (define-key helm-map (kbd "C-i") 'helm-execute-persistent-action)
;; (define-key helm-map (kbd "C-z") 'helm-select-action)
;; (global-set-key (kbd "C-c h o") 'helm-occur)
;; (global-set-key (kbd "C-c h x") 'helm-register))I dislike helm grep immensely (super slow!) and I don’t use ack /
ag / etc. This disables C-c p s <g,a,s> and just does C-c p s
like it used to.
;; (use-package helm-projectile
;; :init
;; (setq projectile-completion-system 'helm)
;; (helm-projectile-on)
;; (define-key projectile-command-map [remap projectile-grep] nil)
;; (define-key projectile-command-map [remap helm-projectile-grep] 'projectile-grep))Mac os x hacks
Get spotlight into list of the files that helm can inspect, and get top output that doesn’t suck;; (when (eq system-type 'darwin)
;; (add-to-list 'helm-for-files-preferred-list 'helm-c-source-mac-spotlight)
;; (setq helm-c-top-command "COLUMNS=%s top -l 1"))Global Tags
Yay gtags support for helm!;; (use-package helm-gtags
;; :config
;; (add-hook 'c-mode-hook (lambda () (helm-gtags-mode)))
;;
;; ;; customize
;; (setq helm-c-gtags-path-style 'absolute)
;; (setq helm-c-gtags-ignore-case t)
;; (setq helm-c-gtags-read-only nil)
;;
;; ;; key bindings
;; (add-hook 'helm-gtags-mode-hook
;; '(lambda ()
;; (local-set-key (kbd "C-c g t") 'helm-gtags-find-tag)
;; (local-set-key (kbd "C-c g r") 'helm-gtags-find-rtag)
;; (local-set-key (kbd "C-c g s") 'helm-gtags-find-symbol)
;; (local-set-key (kbd "C-c g f") 'helm-gtags-find-files)
;; (local-set-key (kbd "C-t") 'helm-gtags-pop-stack))))mu4e
mu4e is a Maildir emacs mail mode. It’s quite simple, but it’s
design goals fall right in the “super perfect” range, and I think it
has a huge opportunity to succeed. It is similar to notmuch.
; (setq message-draft-headers '(From References Subject Date (optional . In-Reply-To) Message-ID (optional . User-Agent)))
; (setq message-generate-headers-first t) ;; would love to do this without Date
; (setq message-auto-save-directory "~/mail/cm/drafts/cur")
; (setq smtpmail-auth-credentials "~/.netrc") ;; is this needed?
; (defun cm/make-dynamic-folder (init)
; (lexical-let ((name init))
; #'(lambda (msg)
; (when (not msg)
; (error (concat "Blank msg in " name)))
; (let ((maildir (mu4e-message-field msg :maildir)))
; (cond
; ((string-match "cm/" maildir)
; (concat "/cm/" name))
; ((string-match "work/" maildir)
; (concat "/work/" name))
; (t
; (error "Could not find message?")))))))
;
; (use-package mu4e
; :bind (("C-c i" . mu4e))
; :init
; (setq mu4e-maildir "~/mail"
; mu4e-sent-folder "/work/sent"
; mu4e-drafts-folder "/cm/drafts"
; mu4e-trash-folder (cm/make-dynamic-folder "trash")
; mu4e-refile-folder (cm/make-dynamic-folder "all")
; mu4e-get-mail-command "mbsync -a" ;; mbsync -a is running by cron
; mu4e-sent-messages-behavior 'sent
; mu4e-org-contacts-file "~/org/_notes/contacts.org"
; mu4e-use-fancy-chars nil
; mu4e-view-show-images t
; mu4e-view-show-addresses t
; mail-user-agent 'mu4e-user-agent
; mu4e-compose-format-flowed t
; fill-flowed-encode-column 60
; mu4e-change-filenames-when-moving t
; mu4e-headers-fields '((:human-date . 12)
; (:flags . 6)
; (:from . 32)
; (:subject ))
; mu4e-headers-time-format "%H:%M"
; mu4e-headers-date-format "%Y-%m-%d"
; mu4e-maildir-shortcuts
; '(("/work/INBOX" . ?i)
; ("/work/all" . ?a)
; ("/work/sent" . ?s)))
; :config
; (setq mu4e-bookmarks
; (list
; (make-mu4e-bookmark
; :name "Combined Inbox"
; :query "maildir:/work/INBOX OR maildir:/cm/INBOX"
; :key ?i)
; (make-mu4e-bookmark
; :name "Flagged"
; :query "flag:flagged"
; :key ?f)
; (make-mu4e-bookmark
; :name "Unread"
; :query "flag:unread"
; :key ?u)
; (make-mu4e-bookmark
; :name "Past Week - All"
; :query "date:8d..1d"
; :key ?w)
; (make-mu4e-bookmark
; :name "Yesterday - All"
; :query "date:2d..1d"
; :key ?y)))
; (require 'org-mu4e)
; (add-to-list 'mu4e-headers-actions
; '("org-contact-add" . mu4e-action-add-org-contact) t)
; (add-to-list 'mu4e-view-actions
; '("org-contact-add" . mu4e-action-add-org-contact) t))Cursor color
Change cursor color according to mode; inspired by http://www.emacswiki.org/emacs/ChangingCursorDynamically valid values are t, nil, box, hollow, bar, (bar . WIDTH), hbar, (hbar. HEIGHT); see the docs for set-cursor-type.This is put at the very end so that way it can override any coloring settings that occur above. Much easier this way.
(defun cm/cursor ()
"change cursor color and type according to some minor modes."
(cond
(buffer-read-only
(setq cursor-type 'hbar))
(t
(set-cursor-color "yellow")
(setq cursor-type 'box))))
(setq cursor-type 'box)
;(add-hook 'post-command-hook 'cm/cursor)
; oh god it flashes so much. Don't know how to do this better yet.
(blink-cursor-mode 0)
(setq cm/cursor-color "#ffff00")
(set-cursor-color cm/cursor-color)
(set-mouse-color cm/cursor-color)
(require 'frame)
(defun cm/cursor-hook (frame)
(set-cursor-color cm/cursor-color)
(modify-frame-parameters
frame (list (cons 'cursor-color cm/cursor-color))))
(add-hook 'after-make-frame-functions 'cm/cursor-hook)Server process
Start a server if it’s not started, and I’m not root.(unless (string-equal "root" (getenv "USER"))
;; Only start server mode if it isn't started already
(unless (server-running-p)
(server-start)))
(add-hook 'server-visit-hook 'raise-frame)
(defun cm/raise-frame-and-give-focus ()
(when window-system
(raise-frame)
(x-focus-frame (selected-frame))
(set-mouse-pixel-position (selected-frame) 4 4)))
(add-hook 'server-switch-hook 'cm/raise-frame-and-give-focus)