126 KB

#+TITLE: Emacs Configuration #+AUTHOR: Bob Mottram #+KEYWORDS: emacs #+DESCRIPTION: My emacs config #+OPTIONS: toc:4 h:4 * Configuration ** About this file <> This configuration file is based upon the example provided by Sacha Chua. See This is an example of /literate programming/ in that it's a mixture of plain English explanations and running code. If you're new to Emacs Lisp, you probably don't want to copy and paste large chunks of this code. Instead, copy small parts of it (always making sure to copy a complete set of parentheses) into your =*scratch*= buffer or some other buffer in =emacs-lisp-mode=. Use =M-x eval-buffer= to evaluate the code and see if you like the way that Emacs behaves. See [[][An Introduction to Programming in Emacs Lisp]] for more details on Emacs Lisp. You can also find the manual by using =C-h i= (=info=) and choosing "Emacs Lisp Intro". I've installed a lot of packages. See the [[Add package sources][package sources]] section to add the repositories to your configuration. When you see =use-package= and a package name you might like, you can use =M-x package-install= to install the package of that name. Note that use-package is itself provided by a package, so you'll probably want to install that and =bind-key=. If you're viewing the Org file, you can open source code blocks (those are the ones in begin_src) in a separate buffer by moving your point inside them and typing C-c ' (=org-edit-special=). This opens another buffer in =emacs-lisp-mode=, so you can use =M-x eval-buffer= to load the changes. If you want to explore how functions work, use =M-x edebug-defun= to set up debugging for that function, and then call it. You can learn more about edebug in the [[][Emacs Lisp]] manual. I like using =(setq ...)= more than Customize because I can neatly organise my configuration that way. ** Personal information #+begin_src emacs-lisp (load "~/.emacs.personal" t) #+end_src Personal information is stored in a separate file, so that the main configuration file can be publicly published. =~/.emacs.personal= should contain something like: #+begin_src emacs-lisp :tangle no (setq user-full-name "myname" user-mail-address "myemailaddress" user-nick "myircnick" user-irc-network "" user-irc-network-name "" user-irc-port 6667 user-irc-channel "" user-irc-password "" user-cities '("Manchester, UK")) #+end_src ** Emacs initialization *** Install packages if necessary ELPA makes it easy to install packages without tracking down all the different websites. Let's define a function that makes it easy to install packages. I don't actually use this a lot any more, but it can be handy. #+begin_src emacs-lisp (defun bashrc/package-install (package &optional repository) "Install PACKAGE if it has not yet been installed. If REPOSITORY is specified, use that." (unless (package-installed-p package) (let ((package-archives (if repository (list (assoc repository package-archives)) package-archives))) (package-install package)))) #+end_src *** Add my elisp directory and other files Sometimes I load files outside the package system. As long as they're in a directory in my =load-path=, Emacs can find them. #+begin_src emacs-lisp (add-to-list 'load-path "~/elisp") (add-to-list 'load-path "~/elisp/org-mode/helm") (add-to-list 'load-path "~/elisp/org-mode/org") (add-to-list 'load-path "~/elisp/use-package") (add-to-list 'load-path "~/elisp/artbollocks-mode") ;;(bashrc/package-install 'use-package) (require 'use-package) #+end_src *** Add package sources #+begin_src emacs-lisp (add-to-list 'package-archives '("org" . "") t) (add-to-list 'package-archives '("melpa" . "") t) #+end_src Use =M-x package-refresh-contents= to reload the list of packages after adding these for the first time. *** Byte-compile my init files to speed things up next time I hardly ever use this because I don't care about my Emacs startup speed. The trick to using Emacs is to not close it. I start Emacs when my computer boots up. I suspend my computer when I go to sleep. Pretty much the only time I shut down is when I need to update the system. #+begin_src emacs-lisp (defun bashrc/byte-recompile () (interactive) (byte-recompile-directory "~/.emacs.d" 0) (byte-recompile-directory "~/elisp" 0)) #+end_src ** General configuration *** static code analysis #+begin_src emacs-lisp (require 'flycheck-clangcheck) (defun my-select-clangcheck-for-checker () "Select clang-check for flycheck's checker." (flycheck-set-checker-executable 'c/c++-clangcheck "/usr/bin/clang-check") (flycheck-select-checker 'c/c++-clangcheck)) (add-hook 'c-mode-hook #'my-select-clangcheck-for-checker) (add-hook 'c++-mode-hook #'my-select-clangcheck-for-checker) ;; enable static analysis (setq flycheck-clangcheck-analyze t) #+end_src *** Inhibit startup screen #+begin_src emacs-lisp (setq inhibit-startup-message t) #+end_src *** Kernel development #+begin_src emacs-lisp (setq c-default-style "linux") #+end_src *** Golden ratio mode #+begin_src emacs-lisp (require 'golden-ratio) (golden-ratio-mode t) #+end_src *** Delimiters #+begin_src emacs-lisp (require 'rainbow-delimiters) (rainbow-delimiters-mode t) #+end_src *** Autocompletion #+begin_src emacs-lisp (require 'auto-complete-config) (add-to-list 'ac-dictionary-directories "~/personal/ac-dict") (ac-config-default) (global-auto-complete-mode t) (auto-complete-mode t) #+end_src *** Font #+begin_src emacs-lisp :tangle no (set-default-font “Noto Sans Mono 24”) #+end_src *** Backups This is one of the things people usually want to change right away. By default, Emacs saves backup files in the current directory. These are the files ending in =~= that are cluttering up your directory lists. The following code stashes them all in =~/.emacs.d/backups=, where I can find them with =C-x C-f= (=find-file=) if I really need to. #+begin_src emacs-lisp (setq backup-directory-alist '(("." . "~/.emacs.d/backups"))) #+end_src Disk space is cheap. Save lots. #+begin_src emacs-lisp (setq delete-old-versions -1) (setq version-control t) (setq vc-make-backup-files t) (setq auto-save-file-name-transforms '((".*" "~/.emacs.d/auto-save-list/" t))) #+end_src *** History From #+begin_src emacs-lisp (setq savehist-file "~/.emacs.d/savehist") (savehist-mode 1) (setq history-length t) (setq history-delete-duplicates t) (setq savehist-save-minibuffer-history 1) (setq savehist-additional-variables '(kill-ring search-ring regexp-search-ring)) #+end_src *** Windows configuration :drill: When you're starting out, tooltips, menus, and the tool bar can be very helpful. [[][(Emacs Basics: Using the Mouse]]). Eventually, you may want to reclaim that extra little bit of screenspace. The following code turns those things off when using a graphical Emacs. #+begin_src emacs-lisp (when window-system (tooltip-mode -1) (tool-bar-mode -1) (menu-bar-mode -1) (scroll-bar-mode -1)) #+end_src *** Winner mode - undo and redo window configuration =winner-mode= lets you use =C-c = and =C-c = to switch between window configurations. This is handy when something has popped up a buffer that you want to look at briefly before returning to whatever you were working on. When you're done, press =C-c =. #+begin_src emacs-lisp (bashrc/package-install 'winner) (use-package winner :config (winner-mode 1)) #+end_src *** Sentences end with a single space In my world, sentences end with a single space. This makes sentence navigation commands work for me. #+begin_src emacs-lisp (setq sentence-end-double-space nil) #+end_src *** Helm - interactive completion Helm makes it easy to complete various things. I find it to be easier to configure than ido in order to get completion in as many places as possible, although I prefer ido's way of switching buffers. #+begin_src emacs-lisp (use-package helm :init (progn (require 'helm-config) (setq helm-candidate-number-limit 100) ;; From (setq helm-idle-delay 0.0 ; update fast sources immediately (doesn't). helm-input-idle-delay 0.01 ; this actually updates things ; reeeelatively quickly. helm-quick-update t helm-M-x-requires-pattern nil helm-ff-skip-boring-files t) (helm-mode 1)) :config (progn ;; I don't like the way switch-to-buffer uses history, since ;; that confuses me when it comes to buffers I've already ;; killed. Let's use ido instead. (add-to-list 'helm-completing-read-handlers-alist '(switch-to-buffer . ido))) (global-set-key (kbd "M-x") 'helm-M-x) :bind (("C-c h" . helm-mini))) (ido-mode -1) ;; Turn off ido mode in case I enabled it accidentally #+end_src #+BEGIN_SRC emacs-lisp (use-package helm :ensure t :init (progn (require 'helm-config) ;; limit max number of matches displayed for speed (setq helm-candidate-number-limit 100) ;; ignore boring files like .o and .a (setq helm-ff-skip-boring-files t) ;; replace locate with spotlight on Mac (setq helm-locate-command "mdfind -name %s %s")) :bind (("C-x f" . helm-for-files))) #+END_SRC Fix problem with org-capture and helm. #+begin_src emacs-lisp (setcdr (assoc 'org-capture helm-completing-read-handlers-alist) nil) #+end_src *** Mode line format Display a more compact mode line #+begin_src emacs-lisp (use-package smart-mode-line :init (progn (setq-default mode-line-format '("%e" mode-line-front-space mode-line-mule-info mode-line-client mode-line-modified mode-line-remote mode-line-frame-identification mode-line-buffer-identification " " mode-line-position (vc-mode vc-mode) " " mode-line-modes mode-line-misc-info mode-line-end-spaces)))) #+end_src Hide minor modes I care less about: #+begin_src emacs-lisp (require 'diminish) (eval-after-load "yasnippet" '(diminish 'yas-minor-mode)) (eval-after-load "undo-tree" '(diminish 'undo-tree-mode)) (eval-after-load "guide-key" '(diminish 'guide-key-mode)) (eval-after-load "smartparens" '(diminish 'smartparens-mode)) (eval-after-load "guide-key" '(diminish 'guide-key-mode)) (eval-after-load "eldoc" '(diminish 'eldoc-mode)) (diminish 'visual-line-mode) #+end_src *** Change "yes or no" to "y or n" Lazy people like me never want to type "yes" when "y" will suffice. #+begin_src emacs-lisp (fset 'yes-or-no-p 'y-or-n-p) #+end_src *** Minibuffer editing - more space! Sometimes you want to be able to do fancy things with the text that you're entering into the minibuffer. Sometimes you just want to be able to read it, especially when it comes to lots of text. This binds =C-M-e= in a minibuffer) so that you can edit the contents of the minibuffer before submitting it. #+begin_src emacs-lisp (use-package miniedit :commands minibuffer-edit :init (miniedit-install)) #+end_src *** Code blocks #+begin_src emacs-lisp (defun org-src-color-blocks-light () "Colors the block headers and footers to make them stand out more for lighter themes" (interactive) (custom-set-faces '(org-block-begin-line ((t (:underline "#A7A6AA" :foreground "#008ED1" :background "#EAEAFF")))) '(org-block-background ((t (:background "#FFFFEA")))) '(org-block ((t (:background "#FFFFEA")))) '(org-block-end-line ((t (:overline "#A7A6AA" :foreground "#008ED1" :background "#EAEAFF")))) '(mode-line-buffer-id ((t (:foreground "#005000" :bold t)))) '(which-func ((t (:foreground "#008000")))))) (defun org-src-color-blocks-dark () "Colors the block headers and footers to make them stand out more for dark themes" (interactive) (custom-set-faces '(org-block-begin-line ((t (:foreground "#008ED1" :background "#002E41")))) '(org-block-background ((t (:background "#000000")))) '(org-block ((t (:background "#000000")))) '(org-block-end-line ((t (:foreground "#008ED1" :background "#002E41")))) '(mode-line-buffer-id ((t (:foreground "black" :bold t)))) '(which-func ((t (:foreground "green")))))) #+end_src *** Theme Use the Yoshi theme. #+begin_src emacs-lisp (load-theme 'yoshi :no-confirm) ;; (interactive) ;; (set-face-foreground 'secondary-selection "darkblue") ;; (set-face-background 'secondary-selection "lightblue") ;; (set-face-background 'font-lock-doc-face "black") ;; (set-face-foreground 'font-lock-doc-face "wheat") ;; (set-face-background 'font-lock-string-face "black") ;; (set-face-foreground 'org-todo "green") ;; (set-face-background 'org-todo "black") #+end_src I sometimes need to switch to a lighter background for screenshots. For that, I use =color-theme-vim=. Make code blocks stand out: #+begin_src emacs-lisp (when window-system (interactive) (custom-set-faces '(org-block-begin-line ((t (:foreground "#008ED1" :background "#002E41")))) '(org-block-background ((t (:background "#000000")))) '(org-block ((t (:background "#000000")))) '(org-block-end-line ((t (:foreground "#008ED1" :background "#002E41")))) '(mode-line-buffer-id ((t (:foreground "black" :bold t)))) '(which-func ((t (:foreground "green")))))) ;;(when window-system ;; (custom-set-faces ;; '(erc-input-face ((t (:foreground "antique white")))) ;; '(helm-selection ((t (:background "ForestGreen" :foreground "black")))) ;; '(org-agenda-clocking ((t (:inherit secondary-selection :foreground "black"))) t) ;; '(org-agenda-done ((t (:foreground "dim gray" :strike-through nil)))) ;; '(org-done ((t (:foreground "PaleGreen" :weight normal :strike-through t)))) ;; '(org-clock-overlay ((t (:background "SkyBlue4" :foreground "black")))) ;; '(org-headline-done ((((class color) (min-colors 16) (background dark)) (:foreground "LightSalmon" :strike-through t)))) ;; '(outline-1 ((t (:inherit font-lock-function-name-face :foreground "cornflower blue")))))) #+end_src *** Undo tree mode - visualize your undos and branches People often struggle with the Emacs undo model, where there's really no concept of "redo" - you simply undo the undo. # This lets you use =C-x u= (=undo-tree-visualize=) to visually walk through the changes you've made, undo back to a certain point (or redo), and go down different branches. #+begin_src emacs-lisp (use-package undo-tree :init (progn (global-undo-tree-mode) (setq undo-tree-visualizer-timestamps t) (setq undo-tree-visualizer-diff t))) #+end_src *** Help - guide-key It's hard to remember keyboard shortcuts. The =guide-key= package pops up help after a short delay. #+begin_src emacs-lisp (use-package guide-key :init (setq guide-key/guide-key-sequence '("C-x r" "C-x 4" "C-c")) (guide-key-mode 1)) ; Enable guide-key-mode #+end_src *** UTF-8 From #+begin_src emacs-lisp (prefer-coding-system 'utf-8) (when (display-graphic-p) (setq x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING))) #+end_src *** Killing text From #+begin_src emacs-lisp (defadvice kill-region (before slick-cut activate compile) "When called interactively with no active region, kill a single line instead." (interactive (if mark-active (list (region-beginning) (region-end)) (list (line-beginning-position) (line-beginning-position 2))))) #+end_src ** Weather reports #+begin_src emacs-lisp (setq wttrin-default-cities user-cities) (setq wttrin-default-accept-language '("Accept-Language" . "en-GB")) #+end_src ** Navigation *** Show line and column numbers for the cursor position #+begin_src emacs-lisp (line-number-mode 1) (column-number-mode 1) #+end_src *** Pop to mark Handy way of getting back to previous places. #+begin_src emacs-lisp (bind-key "C-x p" 'pop-to-mark-command) (setq set-mark-command-repeat-pop t) #+end_src *** Text size #+begin_src emacs-lisp (set-face-attribute 'default nil :height 180) (bind-key "C-+" 'text-scale-increase) (bind-key "C--" 'text-scale-decrease) #+end_src *** Helm-swoop - quickly finding lines This promises to be a fast way to find things. Let's bind it to =Ctrl-Shift-S= to see if I can get used to that... #+begin_src emacs-lisp (use-package helm-swoop :bind (("C-S-s" . helm-swoop))) #+end_src *** Windmove - switching between windows Windmove lets you move between windows with something more natural than cycling through =C-x o= (=other-window=). Windmove doesn't behave well with Org, so we need to use different keybindings. #+begin_src emacs-lisp (use-package windmove :bind ((" " . windmove-right) (" " . windmove-left) (" " . windmove-up) (" " . windmove-down))) #+end_src *** Make window splitting more useful Copied from #+begin_src emacs-lisp (defun bashrc/vsplit-last-buffer (prefix) "Split the window vertically and display the previous buffer." (interactive "p") (split-window-vertically) (other-window 1 nil) (unless prefix (switch-to-next-buffer))) (defun bashrc/hsplit-last-buffer (prefix) "Split the window horizontally and display the previous buffer." (interactive "p") (split-window-horizontally) (other-window 1 nil) (unless prefix (switch-to-next-buffer))) (bind-key "C-x 2" 'bashrc/vsplit-last-buffer) (bind-key "C-x 3" 'bashrc/hsplit-last-buffer) #+end_src *** Searching based on the current word This lets me search up and down. I don't use this often, though. #+begin_src emacs-lisp (defun bashrc/search-word-backward () "Find the previous occurrence of the current word." (interactive) (let ((cur (point))) (skip-syntax-backward "w_") (goto-char (if (re-search-backward (concat "\\_<" (current-word) "\\_>") nil t) (match-beginning 0) cur)))) (defun bashrc/search-word-forward () "Find the next occurrence of the current word." (interactive) (let ((cur (point))) (skip-syntax-forward "w_") (goto-char (if (re-search-forward (concat "\\_<" (current-word) "\\_>") nil t) (match-beginning 0) cur)))) (defadvice search-for-keyword (around bashrc activate) "Match in a case-insensitive way." (let ((case-fold-search t)) ad-do-it)) (global-set-key '[M-up] 'bashrc/search-word-backward) (global-set-key '[M-down] 'bashrc/search-word-forward) #+end_src *** Frequently-accessed files Registers allow you to jump to a file or other location quickly. To jump to a register, use =C-x r j= followed by the letter of the register. #+begin_src emacs-lisp :results silent (mapcar (lambda (r) (set-register (car r) (cons 'file (cdr r)))) '((?i . "~/.emacs.d/") (?o . "~/personal/"))) #+end_src *** Dired From #+begin_src emacs-lisp (require 'find-dired) (setq find-ls-option '("-print0 | xargs -0 ls -ld" . "-ld")) #+end_src *** Move to beginning of line Copied from #+begin_src emacs-lisp (defun bashrc/smarter-move-beginning-of-line (arg) "Move point back to indentation of beginning of line. Move point to the first non-whitespace character on this line. If point is already there, move to the beginning of the line. Effectively toggle between the first non-whitespace character and the beginning of the line. If ARG is not nil or 1, move forward ARG - 1 lines first. If point reaches the beginning or end of the buffer, stop there." (interactive "^p") (setq arg (or arg 1)) ;; Move lines first (when (/= arg 1) (let ((line-move-visual nil)) (forward-line (1- arg)))) (let ((orig-point (point))) (back-to-indentation) (when (= orig-point (point)) (move-beginning-of-line 1)))) ;; remap C-a to `smarter-move-beginning-of-line' (global-set-key [remap move-beginning-of-line] 'bashrc/smarter-move-beginning-of-line) #+end_src *** Recent files #+begin_src emacs-lisp (require 'recentf) (setq recentf-max-saved-items 200 recentf-max-menu-items 15) (recentf-mode) #+end_src *** Copy filename to clipboard #+begin_src emacs-lisp (defun prelude-copy-file-name-to-clipboard () "Copy the current buffer file name to the clipboard." (interactive) (let ((filename (if (equal major-mode 'dired-mode) default-directory (buffer-file-name)))) (when filename (kill-new filename) (message "Copied buffer file name '%s' to the clipboard." filename)))) #+end_src ** Reading From Xah Lee: #+begin_src emacs-lisp (defun xah-toggle-margin-right () "Toggle the right margin between `fill-column' or window width. This command is convenient when reading novel, documentation." (interactive) (if (eq (cdr (window-margins)) nil) (set-window-margins nil 0 (- (window-body-width) fill-column)) (set-window-margins nil 0 0))) #+end_src ** Writing *** Avoiding weasel words #+begin_src emacs-lisp (use-package artbollocks-mode :init (progn (setq artbollocks-weasel-words-regex (concat "\\b" (regexp-opt '("one of the" "should" "just" "sort of" "a lot" "probably" "maybe" "perhaps" "I think" "really" "pretty" "nice" "action" "utilize" "leverage") t) "\\b")) ;; Don't show the art critic words, or at least until I figure ;; out my own jargon (setq artbollocks-jargon nil))) #+end_src *** Unfill paragraph I unfill paragraphs a lot because Wordpress likes adding extra =
= tags if I don't. (I should probably just tweak my Wordpress installation.) #+begin_src emacs-lisp (defun bashrc/unfill-paragraph (&optional region) "Takes a multi-line paragraph and makes it into a single line of text." (interactive (progn (barf-if-buffer-read-only) (list t))) (let ((fill-column (point-max))) (fill-paragraph nil region))) (bind-key "M-Q" 'bashrc/unfill-paragraph) #+end_src I never actually justify text, so I might as well change the way =fill-paragraph= works. With the code below, =M-q= will fill the paragraph normally, and =C-u M-q= will unfill it. #+begin_src emacs-lisp (defun bashrc/fill-or-unfill-paragraph (&optional unfill region) "Fill paragraph (or REGION). With the prefix argument UNFILL, unfill it instead." (interactive (progn (barf-if-buffer-read-only) (list (if current-prefix-arg 'unfill) t))) (let ((fill-column (if unfill (point-max) fill-column))) (fill-paragraph nil region))) (bind-key "M-q" 'bashrc/fill-or-unfill-paragraph) #+end_src Also, =visual-line-mode= is so much better than =auto-fill-mode=. It doesn't actually break the text into multiple lines - it only looks that way. #+begin_src emacs-lisp (remove-hook 'text-mode-hook #'turn-on-auto-fill) (add-hook 'text-mode-hook 'turn-on-visual-line-mode) #+end_src *** Transpose #+begin_src emacs-lisp ;; Transpose stuff with M-t (bind-key "M-t" nil) ;; which used to be transpose-words (bind-key "M-t l" 'transpose-lines) (bind-key "M-t w" 'transpose-words) (bind-key "M-t t" 'transpose-words) (bind-key "M-t M-t" 'transpose-words) (bind-key "M-t s" 'transpose-sexps) #+end_src *** Unicode #+begin_src emacs-lisp (defmacro bashrc/insert-unicode (unicode-name) `(lambda () (interactive) (insert-char (cdr (assoc-string ,unicode-name (ucs-names)))))) (bind-key "C-x 8 s" (bashrc/insert-unicode "ZERO WIDTH SPACE")) (bind-key "C-x 8 S" (bashrc/insert-unicode "SNOWMAN")) #+end_src ** Reading *** nov.el #+begin_src emacs-lisp (use-package nov-mode :commands nov-mode :init (add-to-list 'auto-mode-alist '("\\.epub\\'" . nov-mode))) #+end_src ** Org I use [[][Org Mode]] to take notes, publish my blog, and do all sorts of stuff. *** CalDAV #+begin_src emacs-lisp (setq org-caldav-inbox "~/personal/") #+end_src *** Different sizes/fonts for headers #+begin_src emacs-lisp (let* ((variable-tuple (cond ((x-list-fonts "DejaVu Sans") '(:font "DejaVu Sans")) ((x-list-fonts "Liberation Mono") '(:font "Liberation Mono")) ((x-list-fonts "Liberation Sans") '(:font "Liberation Sans")) ((x-family-fonts "Droid Sans") '(:family "Droid Sans")) (nil (warn "Cannot find a DejaVu Sans Font. Install DejaVu Sans")))) (base-font-color (face-foreground 'default nil 'default)) (headline `(:inherit default :weight bold :foreground ,base-font-color))) (custom-theme-set-faces 'user `(org-level-8 ((t (,@headline ,@variable-tuple)))) `(org-level-7 ((t (,@headline ,@variable-tuple)))) `(org-level-6 ((t (,@headline ,@variable-tuple)))) `(org-level-5 ((t (,@headline ,@variable-tuple)))) `(org-level-4 ((t (,@headline ,@variable-tuple :height 1.1)))) `(org-level-3 ((t (,@headline ,@variable-tuple :height 1.25)))) `(org-level-2 ((t (,@headline ,@variable-tuple :height 1.5)))) `(org-level-1 ((t (,@headline ,@variable-tuple :height 1.75)))) `(org-document-title ((t (,@headline ,@variable-tuple :height 1.5 :underline nil)))))) #+end_src *** Support export as markdown #+begin_src emacs-lisp (eval-after-load "org" '(require 'ox-md nil t)) #+end_src *** Line wrapping #+begin_src emacs-lisp (add-hook 'org-mode-hook '(lambda () (visual-line-mode 1))) #+end_src *** Fancy bullets #+begin_src emacs-lisp (add-hook 'org-mode-hook (lambda () (org-bullets-mode 1))) #+end_src *** Shift select #+begin_src emacs-lisp (setq org-support-shift-select t) #+end_src *** My files #<> Here are the Org files I use, which are stored within the =~/personal= directory. | | My main Org file. Inbox for M-x org-capture, tasks, weekly reviews, etc. | | | Business-related notes and TODOs | | | People-related tasks | | | Projects being worked on | | [[][evil-plans/]] | High-level goals | | [[][sharing/]] | Things to write about | | | Pending, current, and reviewed decisions | | [[][]] | Topic index for my blog | | [[][]] | Learning plan | | | Huge outline of notes by category | | | Temporary Org file for tracking various things | | | Templates for assigning tasks | | | Huge file with book notes | | | Used to use this with ical2org | | | Top level supergoals | | | Archived subtrees | | | Questions, processes, tools | *** Modules Org has a whole bunch of optional modules. These are the ones I'm currently experimenting with. #+begin_src emacs-lisp (setq org-modules '(org-bbdb org-gnus org-drill org-info org-jsinfo org-habit org-irc org-mouse org-annotate-file org-eval org-expiry org-interactive-query org-man org-panel org-screen org-toc)) (org-load-modules-maybe t) (setq org-expiry-inactive-timestamps t) #+end_src *** Keyboard shortcuts #+begin_src emacs-lisp (bind-key "C-c r" 'org-capture) (bind-key "C-c a" 'org-agenda) (bind-key "C-c l" 'org-store-link) (bind-key "C-c L" 'org-insert-link-global) (bind-key "C-c O" 'org-open-at-point-global) (bind-key " " 'org-agenda-list) (bind-key " " (lambda () (interactive) (org-capture nil "r"))) (bind-key "C-TAB" 'org-cycle org-mode-map) (bind-key "C-c v" 'org-show-todo-tree org-mode-map) (bind-key "C-c C-r" 'org-refile org-mode-map) (bind-key "C-c R" 'org-reveal org-mode-map) #+end_src =append-next-kill= is more useful to me than =org-table-copy-region=. #+begin_src emacs-lisp (eval-after-load 'org '(progn (bind-key "C-M-w" 'append-next-kill org-mode-map))) #+end_src I don't use the diary, but I do use the clock a lot. #+begin_src emacs-lisp (use-package org-agenda :init (bind-key "i" 'org-agenda-clock-in org-agenda-mode-map)) #+end_src *** Navigation From #+begin_src emacs-lisp (setq org-goto-interface 'outline org-goto-max-level 10) (require 'imenu) (bind-key "M-o" 'imenu) (bind-key "C-c j" 'org-clock-goto) ;; jump to current task from anywhere (bind-key "C-c C-w" 'org-refile) (setq org-cycle-include-plain-lists 'integrate) #+end_src *** Link Org subtrees and navigate between them The following code makes it easier for me to link trees with entries, as in #+begin_src emacs-lisp (defun bashrc/org-follow-entry-link () "Follow the defined link for this entry." (interactive) (if (org-entry-get (point) "LINK") (org-open-link-from-string (org-entry-get (point) "LINK")) (org-open-at-point))) (bind-key "C-c o" 'bashrc/org-follow-entry-link org-mode-map) (defun bashrc/org-link-projects (location) "Add link properties between the current subtree and the one specified by LOCATION." (interactive (list (let ((org-refile-use-cache nil)) (org-refile-get-location "Location")))) (let ((link1 (org-store-link nil)) link2) (save-window-excursion (org-refile 4 nil location) (setq link2 (org-store-link nil)) (org-set-property "LINK" link1)) (org-set-property "LINK" link2))) #+end_src *** Taking notes My org files are in my =personal= directory. #+begin_src emacs-lisp (setq org-directory "~/personal") (setq org-default-notes-file "~/personal/") #+end_src This makes it easier to add links from outside. #+begin_src emacs-lisp (defun bashrc/yank-more () (interactive) (insert "[[") (yank) (insert "][more]]")) (global-set-key (kbd "") 'bashrc/yank-more) #+end_src **** Templates I use =org-capture= templates to quickly jot down tasks, ledger entries, notes, and other semi-structured pieces of information. #+begin_src emacs-lisp (defvar bashrc/org-basic-task-template "* TODO %^{Task} SCHEDULED: %^t %? :PROPERTIES: :Effort: %^{effort|1:00|0:05|0:15|0:30|2:00|4:00} :END:" "Basic task data") (setq org-capture-templates `(("t" "Tasks" entry (file+headline "~/personal/" "Tasks") ,bashrc/org-basic-task-template) ("b" "Business task" entry (file+headline "~/personal/" "Tasks") ,bashrc/org-basic-task-template) ("p" "People task" entry (file+headline "~/personal/" "Tasks") ,bashrc/org-basic-task-template) ("db" "Done - Business" entry (file+headline "~/personal/" "Tasks") "* DONE %^{Task}\nSCHEDULED: %^t\n%?") ("dp" "Done - People" entry (file+headline "~/personal/" "Tasks") "* DONE %^{Task}\nSCHEDULED: %^t\n%?") ("dt" "Done - Task" entry (file+headline "~/personal/" "Tasks") "* DONE %^{Task}\nSCHEDULED: %^t\n%?") ("q" "Quick note" item (file+headline "~/personal/" "Quick notes")) ("l" "Ledger entries") ("lm" "MBNA" plain (file "~/personal/ledger") "%(org-read-date) %^{Payee} Liabilities:MBNA Expenses:%^{Account} $%^{Amount} " :immediate-finish) ("ln" "No Frills" plain (file "~/personal/ledger") "%(let ((org-read-date-prefer-future nil)) (org-read-date)) * No Frills Liabilities:MBNA Assets:Wayne:Groceries $%^{Amount} " :immediate-finish) ("lc" "Cash" plain (file "~/personal/ledger") "%(org-read-date) * %^{Payee} Expenses:Cash Expenses:%^{Account} %^{Amount} ") ("b" "Book" entry (file+datetree "~/personal/" "Inbox") "* %^{Title} %^g %i ,*Author(s):* %^{Author} \\\\ ,*ISBN:* %^{ISBN} %? ,*Review on:* %^t \\ %a %U" :clock-in :clock-resume) ("c" "Contact" entry (file "~/personal/") "* %(org-contacts-template-name) :PROPERTIES: :EMAIL: %(org-contacts-template-email) :END:") ("n" "Daily note" table-line (file+olp "~/personal/" "Daily notes") "| %u | %^{Note} |" :immediate-finish) ("r" "Notes" entry (file+datetree "~/personal/") "* %?\n\n%i\n" ))) (bind-key "C-M-r" 'org-capture) #+end_src ***** Allow refiling in the middle(ish) of a capture This lets me use =C-c C-r= to refile a capture and then jump to the new location. I wanted to be able to file tasks under projects so that they could inherit the QUANTIFIED property that I use to track time (and any Beeminder-related properties too), but I also wanted to be able to clock in on them. #+begin_src emacs-lisp (defun bashrc/org-capture-refile-and-jump () (interactive) (org-capture-refile) (org-refile-goto-last-stored)) (require 'org-capture) (bind-key "C-c C-r" 'bashrc/org-capture-refile-and-jump org-capture-mode-map) #+end_src **** Refiling =org-refile= lets you organise notes by typing in the headline to file them under. #+begin_src emacs-lisp (setq org-reverse-note-order t) (setq org-refile-use-outline-path nil) (setq org-refile-allow-creating-parent-nodes 'confirm) (setq org-refile-use-cache nil) (setq org-refile-targets '((org-agenda-files . (:maxlevel . 6)))) (setq org-blank-before-new-entry nil) #+end_src **** Estimating WPM I'm curious about how fast I type some things. #+begin_src emacs-lisp (require 'org-clock) (defun bashrc/org-entry-wpm () (interactive) (save-restriction (save-excursion (org-narrow-to-subtree) (goto-char (point-min)) (let* ((words (count-words-region (point-min) (point-max))) (minutes (org-clock-sum-current-item)) (wpm (/ words minutes))) (message "WPM: %d (words: %d, minutes: %d)" wpm words minutes) (kill-new (number-to-string wpm)))))) #+end_src *** Managing tasks **** Track TODO state <> The parentheses indicate keyboard shortcuts that I can use to set the task state. @ and ! toggle logging. #+begin_src emacs-lisp (setq org-todo-keywords '((sequence "TODO(t)" ; next action "TOBLOG(b)" ; next action "STARTED(s)" "ON HOLD(h)" "SOMEDAY(.)" "|" "DONE(x)" "CANCELLED(c)") (sequence "TODELEGATE(-)" "DELEGATED(d)" "COMPLETE(x)"))) #+end_src Colours. #+begin_src emacs-lisp (setq org-todo-keyword-faces '(("TODO" . (:foreground "green" :weight bold)) ("DONE" . (:foreground "cyan" :weight bold)) ("ON HOLD" . (:foreground "red" :weight bold)) ("SOMEDAY" . (:foreground "gray" :weight bold)))) #+end_src **** Projects Projects are headings with the =:project:= tag, so we generally don't want that tag inherited, except when we display unscheduled tasks that don't belong to any projects. #+begin_src emacs-lisp (setq org-tags-exclude-from-inheritance '("project")) #+end_src This code makes it easy for me to focus on one project and its tasks. #+begin_src emacs-lisp (add-to-list 'org-speed-commands-user '("N" org-narrow-to-subtree)) (add-to-list 'org-speed-commands-user '("W" widen)) (defun bashrc/org-agenda-for-subtree () (interactive) (if (derived-mode-p 'org-agenda-mode) (let* ((marker (or (org-get-at-bol 'org-marker) (org-agenda-error))) (hdmarker (or (org-get-at-bol 'org-hd-marker) marker)) (pos (marker-position marker)) (col (current-column)) newhead) (org-with-remote-undo (marker-buffer marker) (with-current-buffer (marker-buffer marker) (widen) (let ((org-agenda-view-columns-initially t)) (org-agenda nil "t" 'subtree))))) (let ((org-agenda-view-columns-initially t)) (org-agenda nil "t" 'subtree)))) (add-to-list 'org-speed-commands-user '("T" bashrc/org-agenda-for-subtree)) #+end_src There's probably a proper way to do this, maybe with =<=. Oh, that would work nicely. =< C-c a t= too. **** Tag tasks with GTD-ish contexts This defines keyboard shortcuts for those, too. #+begin_src emacs-lisp (setq org-tag-alist '(("@work" . ?b) ("@home" . ?h) ("@writing" . ?w) ("@errands" . ?e) ("@drawing" . ?d) ("@coding" . ?c) ("@phone" . ?p) ("@reading" . ?r) ("@computer" . ?l) ("quantified" . ?q) ("lowenergy" . ?0) ("highenergy" . ?1))) #+end_src **** Enable filtering by effort estimates That way, it's easy to see short tasks that I can finish. #+begin_src emacs-lisp (add-to-list 'org-global-properties '("Effort_ALL". "0:05 0:15 0:30 1:00 2:00 3:00 4:00")) #+end_src **** Track time #+begin_src emacs-lisp (setq org-clock-idle-time nil) (setq org-log-done 'time) (setq org-clock-persist t) (org-clock-persistence-insinuate) (setq org-clock-report-include-clocking-task t) (defadvice org-clock-in (after bashrc activate) "Mark STARTED when clocked in." (save-excursion (catch 'exit (cond ((derived-mode-p 'org-agenda-mode) (let* ((marker (or (org-get-at-bol 'org-marker) (org-agenda-error))) (hdmarker (or (org-get-at-bol 'org-hd-marker) marker)) (pos (marker-position marker)) (col (current-column)) newhead) (org-with-remote-undo (marker-buffer marker) (with-current-buffer (marker-buffer marker) (widen) (goto-char pos) (org-back-to-heading t) (if (org-get-todo-state) (org-todo "STARTED")))))) (t (if (org-get-todo-state) (org-todo "STARTED"))))))) #+end_src Too many clock entries clutter up a heading. #+begin_src emacs-lisp (setq org-log-into-drawer "LOGBOOK") (setq org-clock-into-drawer 1) #+end_src **** Habits I like using org-habits to track consistency. My task names tend to be a bit long, though, so I've configured the graph column to show a little bit more to the right. #+begin_src emacs-lisp (setq org-habit-graph-column 80) (setq org-habit-show-habits-only-for-today nil) #+end_src If you want to use habits, be sure to schedule your tasks and add a STYLE property with the value of =habit= to the tasks you want displayed. *** Estimating tasks From "Add an effort estimate on the fly when clocking in" on the [[][Org Hacks]] page: #+begin_src emacs-lisp (add-hook 'org-clock-in-prepare-hook 'bashrc/org-mode-ask-effort) (defun bashrc/org-mode-ask-effort () "Ask for an effort estimate when clocking in." (unless (org-entry-get (point) "Effort") (let ((effort (completing-read "Effort: " (org-entry-get-multivalued-property (point) "Effort")))) (unless (equal effort "") (org-set-property "Effort" effort))))) #+end_src # <> *** Modifying org agenda so that I can display a subset of tasks I want to create an agenda command that displays a list of tasks by context. That way, I can quickly preview a bunch of contexts and decide what I feel like doing the most. #+begin_src emacs-lisp (defvar bashrc/org-agenda-limit-items nil "Number of items to show in agenda to-do views; nil if unlimited.") (defadvice org-agenda-finalize-entries (around bashrc activate) (if bashrc/org-agenda-limit-items (progn (setq list (mapcar 'org-agenda-highlight-todo list)) (setq ad-return-value (subseq list 0 bashrc/org-agenda-limit-items)) (when org-agenda-before-sorting-filter-function (setq list (delq nil (mapcar org-agenda-before-sorting-filter-function list)))) (setq ad-return-value (mapconcat 'identity (delq nil (subseq (sort list 'org-entries-lessp) 0 bashrc/org-agenda-limit-items)) "\n"))) ad-do-it)) #+end_src *** Flexible scheduling of tasks I (theoretically) want to be able to schedule tasks for dates like the first Saturday of every month. Fortunately, [[][someone else has figured that out!]] #+begin_src emacs-lisp ;; Get this from (load "~/elisp/next-spec-day.el" t) #+end_src *** Org calendar #+begin_src emacs-lisp (require 'calfw-cal) (require 'calfw-org) (setq cfw:org-agenda-schedule-args '(:scheduled)) (setq cfw:org-overwrite-default-keybinding t) (define-key org-agenda-mode-map (kbd "M-c") 'cfw:open-org-calendar) (defun my-open-calendar () (interactive) (cfw:open-calendar-buffer :contents-sources (list (cfw:org-create-source "Green") ; orgmode source (cfw:howm-create-source "Blue") ; howm source (cfw:cal-create-source "Orange") ; diary source (cfw:ical-create-source "Moon" "~/moon.ics" "Gray") ; ICS source1 (cfw:ical-create-source "gcal" "https://..../basic.ics" "IndianRed") ; google calendar ICS ))) #+end_src *** Org agenda **** Basic configuration I have quite a few Org files, but I keep my agenda items and TODOs in only a few of them them for faster scanning. #+begin_src emacs-lisp (setq org-agenda-files (delq nil (mapcar (lambda (x) (and (file-exists-p x) x)) '("~/personal/" "~/personal/" "~/personal/" "~/personal/" "~/personal/")))) #+end_src I like looking at a week at a time when I plan using the Org agenda. I want to see my log entries, but I don't want to see scheduled items that I've finished. I like seeing a time grid so that I can get a sense of how appointments are spread out. #+begin_src emacs-lisp (setq org-agenda-span 7) (setq org-agenda-sticky nil) (setq org-agenda-show-log t) (setq org-agenda-skip-scheduled-if-done t) (setq org-agenda-skip-deadline-if-done t) (setq org-agenda-time-grid '((daily today require-timed) "----------------" (800 1000 1200 1400 1600 1800))) (setq org-columns-default-format "%50ITEM %12SCHEDULED %TODO %3PRIORITY %Effort{:} %TAGS") #+end_src Some other keyboard shortcuts: #+begin_src emacs-lisp (bind-key "Y" 'org-agenda-todo-yesterday org-agenda-mode-map) #+end_src # <> **** Starting the week on Monday seems typical #+begin_src emacs-lisp (setq org-agenda-start-on-weekday 1) #+end_src **** Display projects with associated subtasks I wanted a view that showed projects with a few subtasks underneath them. Here's a sample of the output: #+begin_example Headlines with TAGS match: +PROJECT Press `C-u r' to search again with new search string organiser: Set up communication processes for Awesome Foundation Toronto organiser: TODO Announce the next pitch night organiser: TODO Follow up with the winner of the previous pitch night for any news to include in the updates organiser: Tidy up the house so that I can find things quickly organiser: TODO Inventory all the things in closets and boxes :@home: organiser: TODO Drop things off for donation :@errands: organiser: Learn how to develop for Android devices #+end_example #+begin_src emacs-lisp (defun bashrc/org-agenda-project-agenda () "Return the project headline and up to `bashrc/org-agenda-limit-items' tasks." (save-excursion (let* ((marker (org-agenda-new-marker)) (heading (org-agenda-format-item "" (org-get-heading) (org-get-category) nil)) (org-agenda-restrict t) (org-agenda-restrict-begin (point)) (org-agenda-restrict-end (org-end-of-subtree 'invisible)) ;; Find the TODO items in this subtree (list (org-agenda-get-day-entries (buffer-file-name) (calendar-current-date) :todo))) (org-add-props heading (list 'face 'defaults 'done-face 'org-agenda-done 'undone-face 'default 'mouse-face 'highlight 'org-not-done-regexp org-not-done-regexp 'org-todo-regexp org-todo-regexp 'org-complex-heading-regexp org-complex-heading-regexp 'help-echo (format "mouse-2 or RET jump to org file %s" (abbreviate-file-name (or (buffer-file-name (buffer-base-buffer)) (buffer-name (buffer-base-buffer)))))) 'org-marker marker 'org-hd-marker marker 'org-category (org-get-category) 'type "tagsmatch") (concat heading "\n" (org-agenda-finalize-entries list))))) (defun bashrc/org-agenda-projects-and-tasks (match) "Show TODOs for all `org-agenda-files' headlines matching MATCH." (interactive "MString: ") (let ((todo-only nil)) (if org-agenda-overriding-arguments (setq todo-only (car org-agenda-overriding-arguments) match (nth 1 org-agenda-overriding-arguments))) (let* ((org-tags-match-list-sublevels org-tags-match-list-sublevels) (completion-ignore-case t) rtn rtnall files file pos matcher buffer) (when (and (stringp match) (not (string-match "\\S-" match))) (setq match nil)) (setq matcher (org-make-tags-matcher match) match (car matcher) matcher (cdr matcher)) (catch 'exit (if org-agenda-sticky (setq org-agenda-buffer-name (if (stringp match) (format "*Org Agenda(%s:%s)*" (or org-keys (or (and todo-only "M") "m")) match) (format "*Org Agenda(%s)*" (or (and todo-only "M") "m"))))) (org-agenda-prepare (concat "TAGS " match)) (org-compile-prefix-format 'tags) (org-set-sorting-strategy 'tags) (setq org-agenda-query-string match) (setq org-agenda-redo-command (list 'org-tags-view `(quote ,todo-only) (list 'if 'current-prefix-arg nil `(quote ,org-agenda-query-string)))) (setq files (org-agenda-files nil 'ifmode) rtnall nil) (while (setq file (pop files)) (catch 'nextfile (org-check-agenda-file file) (setq buffer (if (file-exists-p file) (org-get-agenda-file-buffer file) (error "No such file %s" file))) (if (not buffer) ;; If file does not exist, error message to agenda (setq rtn (list (format "ORG-AGENDA-ERROR: No such org-file %s" file)) rtnall (append rtnall rtn)) (with-current-buffer buffer (unless (derived-mode-p 'org-mode) (error "Agenda file %s is not in `org-mode'" file)) (save-excursion (save-restriction (if org-agenda-restrict (narrow-to-region org-agenda-restrict-begin org-agenda-restrict-end) (widen)) (setq rtn (org-scan-tags 'bashrc/org-agenda-project-agenda matcher todo-only)) (setq rtnall (append rtnall rtn)))))))) (if org-agenda-overriding-header (insert (org-add-props (copy-sequence org-agenda-overriding-header) nil 'face 'org-agenda-structure) "\n") (insert "Headlines with TAGS match: ") (add-text-properties (point-min) (1- (point)) (list 'face 'org-agenda-structure 'short-heading (concat "Match: " match))) (setq pos (point)) (insert match "\n") (add-text-properties pos (1- (point)) (list 'face 'org-warning)) (setq pos (point)) (unless org-agenda-multi (insert "Press `C-u r' to search again with new search string\n")) (add-text-properties pos (1- (point)) (list 'face 'org-agenda-structure))) (org-agenda-mark-header-line (point-min)) (when rtnall (insert (mapconcat 'identity rtnall "\n") "")) (goto-char (point-min)) (or org-agenda-multi (org-agenda-fit-window-to-buffer)) (add-text-properties (point-min) (point-max) `(org-agenda-type tags org-last-args (,todo-only ,match) org-redo-cmd ,org-agenda-redo-command org-series-cmd ,org-cmd)) (org-agenda-finalize) (setq buffer-read-only t))))) #+end_src # <> **** Org agenda custom commands There are quite a few custom commands here, and I often forget to use them. =) But it's good to define them, and over time, I'll get the hang of using these more! | Key | Description | | . | What am I waiting for? | | T | Not really an agenda command - shows the to-do tree in the current file | | b | Shows business-related tasks | | o | Shows personal tasks and miscellaneous tasks (o: organiser) | | w | Show all tasks for the upcoming week | | W | Show all tasks for the upcoming week, aside from the routine ones | | g ... | Show tasks by context: b - business; c - coding; w - writing; p - phone; d - drawing, h - home | | 0 | Show common contexts with up to 3 tasks each, so that I can choose what I feel like working on | | ) (shift-0) | Show common contexts with all the tasks associated with them | | 9 | Show common contexts with up to 3 unscheduled tasks each | | ( (shift-9) | Show common contexts with all the unscheduled tasks associated with them | | d | Timeline for today (agenda, clock summary) | | u | Unscheduled tasks to do if I have free time | | U | Unscheduled tasks that are not part of projects | | P | Tasks by priority | | p | My projects | | 2 | Projects with tasks | #+begin_src emacs-lisp (defvar bashrc/org-agenda-contexts '((tags-todo "+@phone") (tags-todo "+@work") (tags-todo "+@drawing") (tags-todo "+@coding") (tags-todo "+@writing") (tags-todo "+@computer") (tags-todo "+@home") (tags-todo "+@errands")) "Usual list of contexts.") (defun bashrc/org-agenda-skip-scheduled () (org-agenda-skip-entry-if 'scheduled 'deadline 'regexp "\n]+>")) (setq org-agenda-custom-commands `(("T" tags-todo "TODO=\"TODO\"-routine") ("b" todo "" ((org-agenda-files '("~/personal/")))) ("o" todo "" ((org-agenda-files '("~/personal/")))) ("c" todo "" ((org-agenda-prefix-format "") (org-agenda-cmp-user-defined 'bashrc/org-sort-agenda-items-todo) (org-agenda-view-columns-initially t) )) ;; Weekly review ("w" "Weekly review" agenda "" ((org-agenda-span 7) (org-agenda-log-mode 1))) ("W" "Weekly review sans routines" agenda "" ((org-agenda-span 7) (org-agenda-log-mode 1) (org-agenda-tag-filter-preset '("-routine")))) ("2" "Bi-weekly review" agenda "" ((org-agenda-span 14) (org-agenda-log-mode 1))) ("gb" "Business" todo "" ((org-agenda-files '("~/personal/")) (org-agenda-view-columns-initially t))) ("gc" "Coding" tags-todo "@coding" ((org-agenda-view-columns-initially t))) ("gw" "Writing" tags-todo "@writing" ((org-agenda-view-columns-initially t))) ("gp" "Phone" tags-todo "@phone" ((org-agenda-view-columns-initially t))) ("gd" "Drawing" tags-todo "@drawing" ((org-agenda-view-columns-initially t))) ("gh" "Home" tags-todo "@home" ((org-agenda-view-columns-initially t))) ("ge" "Errands" tags-todo "@errands" ((org-agenda-view-columns-initially t))) ("0" "Top 3 by context" ,bashrc/org-agenda-contexts ((org-agenda-sorting-strategy '(priority-up effort-down)) (bashrc/org-agenda-limit-items 3))) (")" "All by context" ,bashrc/org-agenda-contexts ((org-agenda-sorting-strategy '(priority-down effort-down)) (bashrc/org-agenda-limit-items nil))) ("9" "Unscheduled top 3 by context" ,bashrc/org-agenda-contexts ((org-agenda-skip-function 'bashrc/org-agenda-skip-scheduled) (org-agenda-sorting-strategy '(priority-down effort-down)) (bashrc/org-agenda-limit-items 3))) ("(" "All unscheduled by context" ,bashrc/org-agenda-contexts ((org-agenda-skip-function 'bashrc/org-agenda-skip-scheduled) (org-agenda-sorting-strategy '(priority-down effort-down)) )) ("d" "Timeline for today" ((agenda "" )) ((org-agenda-ndays 1) (org-agenda-show-log t) (org-agenda-log-mode-items '(clock closed)) (org-agenda-clockreport-mode t) (org-agenda-entry-types '()))) ("." "Waiting for" todo "ON HOLD") ("u" "Unscheduled tasks" tags-todo "-someday-TODO=\"SOMEDAY\"-TODO=\"DELEGATED\"-TODO=\"ON HOLD\"-project" ((org-agenda-skip-function 'bashrc/org-agenda-skip-scheduled) (org-agenda-view-columns-initially t) (org-tags-exclude-from-inheritance '("project")) (org-agenda-overriding-header "Unscheduled TODO entries: ") (org-columns-default-format "%50ITEM %TODO %3PRIORITY %Effort{:} %TAGS") (org-agenda-sorting-strategy '(todo-state-up priority-down effort-up tag-up category-keep)))) ("U" "Unscheduled tasks outside projects" tags-todo "-project" ((org-agenda-skip-function 'bashrc/org-agenda-skip-scheduled) (org-tags-exclude-from-inheritance nil) (org-agenda-view-columns-initially t) (org-agenda-overriding-header "Unscheduled TODO entries outside projects: ") (org-agenda-sorting-strategy '(todo-state-up priority-down tag-up category-keep effort-down)))) ("P" "By priority" ((tags-todo "+PRIORITY=\"A\"") (tags-todo "+PRIORITY=\"B\"") (tags-todo "+PRIORITY=\"\"") (tags-todo "+PRIORITY=\"C\"")) ((org-agenda-prefix-format "%-10c %-10T %e ") (org-agenda-sorting-strategy '(priority-down tag-up category-keep effort-down)))) ("pp" tags "+project-someday-TODO=\"DONE\"" ((org-tags-exclude-from-inheritance '("project")) (org-agenda-sorting-strategy '(priority-down tag-up category-keep effort-down)))) ("p." tags "+project-TODO=\"DONE\"" ((org-tags-exclude-from-inheritance '("project")) (org-agenda-sorting-strategy '(priority-down tag-up category-keep effort-down)))) ("S" tags-todo "TODO=\"STARTED\"") ("2" "List projects with tasks" bashrc/org-agenda-projects-and-tasks "+PROJECT" ((bashrc/org-agenda-limit-items 3))))) (bind-key " a" 'org-agenda) #+end_src **** Make it easy to mark a task as done Great for quickly going through the to-do list. Gets rid of one extra keystroke. ;) #+begin_src emacs-lisp (defun bashrc/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")) ;; Override the key definition for org-exit (define-key org-agenda-mode-map "x" 'bashrc/org-agenda-done) #+end_src **** Archive done items #+begin_src emacs-lisp (defun clear-done-tasks () (interactive) (org-map-entries 'org-archive-subtree "/DONE" 'file)) #+end_src **** Make it easy to mark a task as done and create a follow-up task #+begin_src emacs-lisp (defun bashrc/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")) ;; Override the key definition (define-key org-agenda-mode-map "X" 'bashrc/org-agenda-mark-done-and-add-followup) #+end_src **** Capture something based on the agenda #+begin_src emacs-lisp (defun bashrc/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)) ;; New key assignment (define-key org-agenda-mode-map "N" 'bashrc/org-agenda-new) #+end_src **** Sorting by date and priority #+begin_src emacs-lisp (setq org-agenda-sorting-strategy '((agenda time-up priority-down tag-up effort-up category-keep) (todo user-defined-up todo-state-up priority-down effort-up) (tags user-defined-up) (search category-keep))) (setq org-agenda-cmp-user-defined 'bashrc/org-sort-agenda-items-user-defined) (require 'cl) (defun bashrc/org-get-context (txt) "Find the context." (car (member-if (lambda (item) (string-match "@" item)) (get-text-property 1 'tags txt)))) (defun bashrc/org-compare-dates (a b) "Return 1 if A should go after B, -1 if B should go after A, or 0 if a = b." (cond ((and (= a 0) (= b 0)) nil) ((= a 0) 1) ((= b 0) -1) ((> a b) 1) ((< a b) -1) (t nil))) (defun bashrc/org-complete-cmp (a b) (let* ((state-a (or (get-text-property 1 'todo-state a) "")) (state-b (or (get-text-property 1 'todo-state b) ""))) (or (if (member state-a org-done-keywords-for-agenda) 1) (if (member state-b org-done-keywords-for-agenda) -1)))) (defun bashrc/org-date-cmp (a b) (let* ((sched-a (or (get-text-property 1 'org-scheduled a) 0)) (sched-b (or (get-text-property 1 'org-scheduled b) 0)) (deadline-a (or (get-text-property 1 'org-deadline a) 0)) (deadline-b (or (get-text-property 1 'org-deadline b) 0))) (or (bashrc/org-compare-dates (bashrc/org-min-date sched-a deadline-a) (bashrc/org-min-date sched-b deadline-b))))) (defun bashrc/org-min-date (a b) "Return the smaller of A or B, except for 0." (funcall (if (and (> a 0) (> b 0)) 'min 'max) a b)) (defun bashrc/org-sort-agenda-items-user-defined (a b) ;; compare by deadline, then scheduled date; done tasks are listed at the very bottom (or (bashrc/org-complete-cmp a b) (bashrc/org-date-cmp a b))) (defun bashrc/org-context-cmp (a b) "Compare CONTEXT-A and CONTEXT-B." (let ((context-a (bashrc/org-get-context a)) (context-b (bashrc/org-get-context b))) (cond ((null context-a) +1) ((null context-b) -1) ((string< context-a context-b) -1) ((string< context-b context-a) +1) (t nil)))) (defun bashrc/org-sort-agenda-items-todo (a b) (or (org-cmp-time a b) (bashrc/org-complete-cmp a b) (bashrc/org-context-cmp a b) (bashrc/org-date-cmp a b) (org-cmp-todo-state a b) (org-cmp-priority a b) (org-cmp-effort a b))) #+end_src **** Preventing things from falling through the cracks This helps me keep track of unscheduled tasks, because I sometimes forget to assign tasks a date. I also want to keep track of stuck projects. #+begin_src emacs-lisp (defun bashrc/org-agenda-list-unscheduled (&rest ignore) "Create agenda view for tasks that are unscheduled and not done." (let* ((org-agenda-todo-ignore-with-date t) (org-agenda-overriding-header "List of unscheduled tasks: ")) (org-agenda-get-todos))) (setq org-stuck-projects '("+PROJECT-MAYBE-DONE" ("TODO") nil "\\")) #+end_src *** Weekly review <> :PROPERTIES: :CUSTOM_ID: weekly-review :END: I regularly post [[][weekly reviews]] to keep track of what I'm done, remind me to plan for the upcoming week, and list blog posts, sketches, and links. I I want to try out grouping tasks by topic first, then breaking it down into previous/next week. #+begin_src emacs-lisp (defvar bashrc/weekly-review-line-regexp "^ \\([^:]+\\): +\\(Sched[^:]+: +\\)?TODO \\(.*?\\)\\(?:[ ]+\\(:[[:alnum:]_@#%:]+:\\)\\)?[ ]*$" "Regular expression matching lines to include.") (defvar bashrc/weekly-done-line-regexp "^ \\([^:]+\\): +.*?\\(?:Clocked\\|Closed\\):.*?\\(?:TODO\\|DONE\\) \\(.*?\\)\\(?:[ ]+\\(:[[:alnum:]_@#%:]+:\\)\\)?[ ]*$" "Regular expression matching lines to include as completed tasks.") (defun bashrc/quantified-get-hours (category time-summary) "Return the number of hours based on the time summary." (if (stringp category) (if (assoc category time-summary) (/ (cdr (assoc category time-summary)) 3600.0) 0) (apply '+ (mapcar (lambda (x) (bashrc/quantified-get-hours x time-summary)) category)))) (defun bashrc/org-summarize-focus-areas () "Summarize previous and upcoming tasks as a list." (interactive) (let ((base-date (apply 'encode-time (org-read-date-analyze "-fri" nil '(0 0 0)))) (line-re bashrc/weekly-review-line-regexp) (done-re bashrc/weekly-done-line-regexp) business relationships life business-next relationships-next life-next string start end time-summary biz-time) (setq start (format-time-string "%Y-%m-%d" (days-to-time (- (time-to-number-of-days base-date) 6)))) (setq end (format-time-string "%Y-%m-%d" (days-to-time (1+ (time-to-number-of-days base-date))))) (setq time-summary (quantified-summarize-time start end)) (setq biz-time (bashrc/quantified-get-hours "Business" time-summary)) (save-window-excursion (org-agenda nil "W") (setq string (buffer-string)) (with-temp-buffer (insert string) (goto-char (point-min)) (while (re-search-forward line-re nil t) (cond ((string= (match-string 1) "routines") nil) ; skip routine tasks ((or (string= (match-string 1) "business") (string= (match-string 1) "tasks")) (add-to-list 'business-next (concat " - [ ] " (match-string 3)))) ((string= (match-string 1) "people") (add-to-list 'relationships-next (concat " - [ ] " (match-string 3)))) (t (add-to-list 'life-next (concat " - [ ] " (match-string 3)))))))) (save-window-excursion (org-agenda nil "W") (org-agenda-later -1) (org-agenda-log-mode 16) (setq string (buffer-string)) ;; Get any completed tasks from the current week as well (org-agenda-later 1) (org-agenda-log-mode 16) (setq string (concat string "\n" (buffer-string))) (with-temp-buffer (insert string) (goto-char (point-min)) (while (re-search-forward done-re nil t) (cond ((string= (match-string 1) "routines") nil) ; skip routine tasks ((or (string= (match-string 1) "business") (string= (match-string 1) "tasks")) (add-to-list 'business (concat " - [X] " (match-string 2)))) ((string= (match-string 1) "people") (add-to-list 'relationships (concat " - [X] " (match-string 2)))) (t (add-to-list 'life (concat " - [X] " (match-string 2)))))))) (setq string (concat (format "- *Business* (%.1fh - %d%%)\n" biz-time (/ biz-time 1.68)) (mapconcat 'identity (sort business 'string<) "\n") "\n" (mapconcat 'identity (sort business-next 'string<) "\n") "\n" (format " - *Earn* (%.1fh - %d%% of Business)\n" (bashrc/quantified-get-hours "Business - Earn" time-summary) (/ (bashrc/quantified-get-hours "Business - Earn" time-summary) (* 0.01 biz-time))) (format " - *Build* (%.1fh - %d%% of Business)\n" (bashrc/quantified-get-hours "Business - Build" time-summary) (/ (bashrc/quantified-get-hours "Business - Build" time-summary) (* 0.01 biz-time))) (format " - *Drawing* (%.1fh)\n" (bashrc/quantified-get-hours '("Business - Build - Drawing" "Business - Build - Book review") time-summary)) (format " - *Delegation* (%.1fh)\n" (bashrc/quantified-get-hours "Business - Build - Delegation" time-summary)) (format " - *Packaging* (%.1fh)\n" (bashrc/quantified-get-hours "Business - Build - Packaging" time-summary)) (format " - *Paperwork* (%.1fh)\n" (bashrc/quantified-get-hours "Business - Build - Paperwork" time-summary)) (format " - *Connect* (%.1fh - %d%% of Business)\n" (bashrc/quantified-get-hours "Business - Connect" time-summary) (/ (bashrc/quantified-get-hours "Business - Connect" time-summary) (* 0.01 biz-time))) (format "- *Relationships* (%.1fh - %d%%)\n" (bashrc/quantified-get-hours '("Discretionary - Social" "Discretionary - Family") time-summary) (/ (bashrc/quantified-get-hours '("Discretionary - Social" "Discretionary - Family") time-summary) 1.68)) (mapconcat 'identity (sort relationships 'string<) "\n") "\n" (mapconcat 'identity (sort relationships-next 'string<) "\n") "\n" (format "- *Discretionary - Productive* (%.1fh - %d%%)\n" (bashrc/quantified-get-hours "Discretionary - Productive" time-summary) (/ (bashrc/quantified-get-hours "Discretionary - Productive" time-summary) 1.68)) (format " - *Emacs* (%.1fh - %d%% of all)\n" (bashrc/quantified-get-hours "Discretionary - Productive - Emacs" time-summary) (/ (bashrc/quantified-get-hours "Discretionary - Productive - Emacs" time-summary) 1.68)) (mapconcat 'identity (sort life 'string<) "\n") "\n" (mapconcat 'identity (sort life-next 'string<) "\n") "\n" (format " - *Writing* (%.1fh)\n" (bashrc/quantified-get-hours "Discretionary - Productive - Writing" time-summary)) (format "- *Discretionary - Play* (%.1fh - %d%%)\n" (bashrc/quantified-get-hours "Discretionary - Play" time-summary) (/ (bashrc/quantified-get-hours "Discretionary - Play" time-summary) 1.68)) ; (format "- *Discretionary - Travel* (%.1fh - %d%%)\n" ; (bashrc/quantified-get-hours "Discretionary - Travel" time-summary) ; (/ (bashrc/quantified-get-hours "Discretionary - Travel" time-summary) 1.68)) (format "- *Personal routines* (%.1fh - %d%%)\n" (bashrc/quantified-get-hours "Personal" time-summary) (/ (bashrc/quantified-get-hours "Personal" time-summary) 1.68)) (format "- *Unpaid work* (%.1fh - %d%%)\n" (bashrc/quantified-get-hours "Unpaid work" time-summary) (/ (bashrc/quantified-get-hours "Unpaid work" time-summary) 1.68)) (format "- *Sleep* (%.1fh - %d%% - average of %.1f per day)\n" (bashrc/quantified-get-hours "Sleep" time-summary) (/ (bashrc/quantified-get-hours "Sleep" time-summary) 1.68) (/ (bashrc/quantified-get-hours "Sleep" time-summary) 7) ))) (if (called-interactively-p 'any) (insert string) string))) #+end_src I use this to put together a quick summary of how I spent my time. The following code makes it easy to add a line: #+begin_src emacs-lisp (defun bashrc/org-add-line-item-task (task) (interactive "MTask: ") (org-insert-heading) (insert "[ ] " task) (let ((org-capture-entry '("t" "Tasks" entry (file+headline "~/personal/" "Tasks") ""))) (org-capture nil "t") (insert "TODO " task "\nSCHEDULED: <" (org-read-date) ">"))) ;(define-key org-mode-map (kbd "C-c t") 'bashrc/org-add-line-item-task) #+end_src Now we put it all together... #+begin_src emacs-lisp (defun bashrc/org-prepare-weekly-review () "Prepare weekly review template." (interactive) (let ((base-date (apply 'encode-time (org-read-date-analyze "-fri" nil '(0 0 0)))) start end) (setq start (format-time-string "%Y-%m-%d" (days-to-time (- (time-to-number-of-days base-date) 6)))) (setq end (format-time-string "%Y-%m-%d" (days-to-time (1+ (time-to-number-of-days base-date))))) (insert (concat "*** Weekly review: Week ending " (format-time-string "%B %e, %Y" base-date) " :weekly:\n" "*Blog posts*\n\n" "\n\n*Focus areas and time review*\n\n" (bashrc/org-summarize-focus-areas) "\n")))) #+end_src **** Link-related convenience functions #+begin_src emacs-lisp (defun kensanata/resolve-redirect (url) "Resolve shortened URL by launching `curl --head' and parsing the result." (let* ((curl (shell-command-to-string (format "curl --silent --head %s" url))) (location (when (and (string-match "^HTTP/1\.1 301" curl) (string-match "^Location: \\(.*\\)" curl)) (match-string 1 curl)))) (or location url))) (defun bashrc/resolve-urls-in-region (beg end) "Expand URLs between BEG and END." (interactive "r") (save-excursion (save-restriction (narrow-to-region beg end) (goto-char (point-min)) (while (re-search-forward org-bracket-link-regexp nil t) (replace-match (save-match-data (kensanata/resolve-redirect (match-string 1))) t t nil 1)) (goto-char (point-min)) (while (re-search-forward org-link-re-with-space nil t) (replace-match (save-match-data (kensanata/resolve-redirect (match-string 0))) t t nil))))) (defun bashrc/open-urls-in-region (beg end) "Open URLs between BEG and END." (interactive "r") (save-excursion (save-restriction (narrow-to-region beg end) (goto-char (point-min)) (while (re-search-forward org-plain-link-re nil t) (org-open-at-point))))) #+end_src *** Moving lines around This makes it easier to reorganise lines in my weekly review. #+begin_src emacs-lisp (defun bashrc/org-move-line-to-destination () "Moves the current list item to <> in the current buffer." (interactive) (save-window-excursion (save-excursion (let ((string (buffer-substring-no-properties (line-beginning-position) (line-end-position)))) (delete-region (line-beginning-position) (1+ (line-end-position))) (goto-char (point-min)) (re-search-forward "<>" nil t) (insert "\n" (make-string (- (match-beginning 0) (line-beginning-position)) ?\ ) (s-trim string)))))) (bind-key "C-c d" 'bashrc/org-move-line-to-destination org-mode-map) #+end_src *** Monthly reviews <> I want to be able to see what I worked on in a month so that I can write my [[][monthly reviews]]. This code makes it easy to display a month's clocked tasks and time. I haven't been particularly thorough in tracking time before, but now that I have a shortcut that logs in Quantified Awesome as well as in Org, I should end up clocking more. #+begin_src emacs-lisp (defun bashrc/org-review-month (start-date) "Review the month's clocked tasks and time." (interactive (list (org-read-date))) ;; Set to the beginning of the month (setq start-date (concat (substring start-date 0 8) "01")) (let ((org-agenda-show-log t) (org-agenda-start-with-log-mode t) (org-agenda-start-with-clockreport-mode t) (org-agenda-clockreport-parameter-plist '(:link t :maxlevel 3))) (org-agenda-list nil start-date 'month))) #+end_src *** Viewing, navigating, and editing the Org tree I often cut and paste subtrees. This makes it easier to cut something and paste it elsewhere in the hierarchy. #+begin_src emacs-lisp (eval-after-load 'org '(progn (bind-key "C-c k" 'org-cut-subtree org-mode-map) (setq org-yank-adjusted-subtrees t))) #+end_src *** Organise my blog index #+begin_src emacs-lisp (defun bashrc/org-file-blog-index-entries (beg end location) "Copy entries into" (interactive (list (if (region-active-p) (point) (line-beginning-position)) (if (region-active-p) (mark) (1+ (line-end-position))) (let ((org-refile-targets '(("~/sharing/" . (:maxlevel . 3))))) (org-refile-get-location "Location")))) (let ((s (replace-regexp-in-string "^ *- " "- [X] " (buffer-substring-no-properties beg end)))) (save-window-excursion (save-excursion (find-file (nth 1 location)) (save-excursion (save-restriction (widen) (goto-char (nth 3 location)) (looking-at org-outline-regexp) (forward-line 1) (insert s) (org-update-statistics-cookies nil))))))) (bind-key "C-c f" 'bashrc/org-file-blog-index-entries org-mode-map) #+end_src *** Publishing Timestamps and section numbers make my published files look more complicated than they are. Let's turn them off by default. #+begin_src emacs-lisp (setq org-export-with-section-numbers nil) (setq org-html-include-timestamps nil) #+end_src This makes it easier to publish my files: #+begin_src emacs-lisp (if (string= system-name "webdev") (setq bashrc/emacs-notes-directory "~/code/dev/emacs-notes") (setq bashrc/emacs-notes-directory "~/code/dev/emacs-notes")) (setq org-publish-project-alist '(("public" :base-directory "~/public" :publishing-directory "~/public" :publishing-function bashrc/org-html-publish-to-html-trustingly ) ("sharing" :base-directory "~/public/sharing" :publishing-directory "~/public/sharing" :publishing-function bashrc/org-html-publish-to-html-trustingly ) ("emacs-config" :base-directory "~/.emacs.d" :publishing-directory "~/.emacs.d" :publishing-function bashrc/org-html-publish-to-html-trustingly ))) (load "~/code/dev/emacs-chats/build-site.el" t) (load "~/code/dev/emacs-notes/build-site.el" t) #+end_src If a file is in a publishing project, publish it. #+begin_src emacs-lisp (defun bashrc/org-publish-maybe () (interactive) (save-excursion (when (org-publish-get-project-from-filename (buffer-file-name (buffer-base-buffer)) 'up) (org-publish-current-file)))) (bind-key "C-c C-p C-p" 'bashrc/org-publish-maybe org-mode-map) #+end_src Make it easy to publish and browse a file. #+begin_src emacs-lisp (defun bashrc/org-publish-and-browse () (interactive) (save-buffer) (bashrc/org-publish-maybe) (browse-url (org-export-output-file-name ".html" nil default-directory))) (bind-key " b" 'bashrc/org-publish-and-browse) #+end_src **** Publish without prompting I want to be able to export without having to say yes to code blocks all the time. #+begin_src emacs-lisp (defun bashrc/org-html-export-trustingly () (interactive) (let ((org-confirm-babel-evaluate nil)) (org-html-export-to-html))) (defun bashrc/org-html-publish-to-html-trustingly (plist filename pub-dir) (let ((org-confirm-babel-evaluate nil)) (org-html-publish-to-html plist filename pub-dir))) #+end_src **** Stylesheet / header Might as well take advantage of my stylesheet: #+begin_src emacs-lisp (setq org-html-head " ") (setq org-html-htmlize-output-type 'css) (setq org-src-fontify-natively t) #+end_src **** Footer Make it easy to scroll to the top: #+begin_src emacs-lisp (setq org-html-preamble "") (setq org-html-postamble "
Back to top | E-mail me
") #+end_src **** Copy region Sometimes I want a region's HTML in my kill-ring/clipboard without any of the extra fluff: #+begin_src emacs-lisp (defun bashrc/org-copy-region-as-html (beg end &optional level) "Make it easier to copy code for Wordpress posts and other things." (interactive "r\np") (let ((org-export-html-preamble nil) (org-html-toplevel-hlevel (or level 3))) (kill-new (org-export-string-as (buffer-substring beg end) 'html t)))) #+end_src Sometimes I want a subtree: #+begin_src emacs-lisp (defun bashrc/org-copy-subtree-as-html () (interactive) (bashrc/org-copy-region-as-html (org-back-to-heading) (org-end-of-subtree))) #+end_src **** UTF-8 checkboxes This snippet turns =- [X]= into ☑ and =- [ ]= into ☐, but leaves =[-]= alone. #+begin_src emacs-lisp (setq org-html-checkbox-types 'unicode) (setq org-html-checkbox-types '((unicode (on . "") (off . "") (trans . "[-]")))) #+end_src *** Structure templates Org makes it easy to insert blocks by typing =\n\n") ("e" "#+begin_example\n?\n#+end_example" "\n?\n") ("q" "#+begin_quote\n?\n#+end_quote" "\n?\n") ("v" "#+BEGIN_VERSE\n?\n#+END_VERSE" "\n?\n") ("c" "#+BEGIN_COMMENT\n?\n#+END_COMMENT") ("p" "#+BEGIN_PRACTICE\n?\n#+END_PRACTICE") ("l" "#+begin_src emacs-lisp\n?\n#+end_src" "\n?\n") ("L" "#+latex: " "?") ("h" "#+begin_html\n?\n#+end_html" "\n?\n") ("H" "#+html: " "?") ("a" "#+begin_ascii\n?\n#+end_ascii") ("A" "#+ascii: ") ("i" "#+index: ?" "#+index: ?") ("I" "#+include %file ?" ""))) #+end_src *** Quick links #+begin_src emacs-lisp (setq org-link-abbrev-alist '(("search" . "") ("map" . "") ("blog" . ""))) #+end_src *** Speed commands These are great for quickly acting on tasks. #+begin_src emacs-lisp (setq org-use-effective-time t) (defun bashrc/org-use-speed-commands-for-headings-and-lists () "Activate speed commands on list items too." (or (and (looking-at org-outline-regexp) (looking-back "^\**")) (save-excursion (and (looking-at (org-item-re)) (looking-back "^[ \t]*"))))) (setq org-use-speed-commands 'bashrc/org-use-speed-commands-for-headings-and-lists) (add-to-list 'org-speed-commands-user '("x" org-todo "DONE")) (add-to-list 'org-speed-commands-user '("y" org-todo-yesterday "DONE")) (add-to-list 'org-speed-commands-user '("!" bashrc/org-clock-in-and-track)) (add-to-list 'org-speed-commands-user '("s" call-interactively 'org-schedule)) (add-to-list 'org-speed-commands-user '("d" bashrc/org-move-line-to-destination)) (add-to-list 'org-speed-commands-user '("i" call-interactively 'org-clock-in)) (add-to-list 'org-speed-commands-user '("o" call-interactively 'org-clock-out)) (bind-key "!" 'bashrc/org-clock-in-and-track org-agenda-mode-map) #+end_src *** Attachments Org lets you attach files to an Org file. Haven't gotten the hang of this yet, but looks interesting. #+begin_src emacs-lisp (setq org-attach-store-link-p 'attached) (setq org-attach-auto-tag nil) #+end_src *** Counting Good way to remind myself that I have lots of STARTED tasks. #+begin_src emacs-lisp (defun bashrc/org-summarize-task-status () "Count number of tasks by status. Probably should make this a dblock someday." (interactive) (let (result) (org-map-entries (lambda () (let ((todo (elt (org-heading-components) 2))) (if todo (if (assoc todo result) (setcdr (assoc todo result) (1+ (cdr (assoc todo result)))) (setq result (cons (cons todo 1) result))))))) (message "%s" (mapconcat (lambda (x) (format "%s: %d" (car x) (cdr x))) result "\n")))) #+end_src *** Diagrams and graphics Ooooh. Graphviz and Ditaa make it easier to create diagrams from Emacs. See [[]] for examples and source. #+begin_src emacs-lisp (setq org-ditaa-jar-path "/usr/bin/ditaa.jar") (setq org-startup-with-inline-images t) (add-hook 'org-babel-after-execute-hook 'org-display-inline-images) (org-babel-do-load-languages 'org-babel-load-languages '((dot . t) (ditaa . t) (sh . t) (js . t) (emacs-lisp . t) (perl . t) (scala . t) (clojure . t) (python . t) (ruby . t) (dot . t) (C . t) (rust . t) (css . t) (R . t))) (setq org-babel-sh-command "bash") (add-to-list 'org-src-lang-modes '("dot" . graphviz-dot)) #+end_src *** Presentations #+begin_src emacs-lisp (use-package ox-reveal) #+end_src *** Task dependencies #+begin_src emacs-lisp (setq org-enforce-todo-dependencies t) (setq org-track-ordered-property-with-tag t) #+end_src ** Markdown #+begin_src emacs-lisp (autoload 'markdown-mode "markdown-mode" "Major mode for editing Markdown files" t) (add-to-list 'auto-mode-alist '("\\.markdown\\'" . markdown-mode)) (add-to-list 'auto-mode-alist '("\\.md\\'" . markdown-mode)) (autoload 'gfm-mode "markdown-mode" "Major mode for editing GitHub Flavored Markdown files" t) (add-to-list 'auto-mode-alist '("README\\.md\\'" . gfm-mode)) #+end_src ** IRC (ERC) Proxy via Tor. #+begin_src emacs-lisp (setq socks-password "") (setq socks-noproxy '("localhost")) (require 'socks) (require 'tls) (setq socks-server (list "Tor socks" "localhost" 9050 5)) (setq url-gateway-method 'socks) (setq erc-server-connect-function 'socks-open-network-stream) #+end_src Channels to join: #+begin_src emacs-lisp (if (not (eq user-irc-network '"")) (setq erc-autojoin-channels-alist (list (list user-irc-network-name user-irc-channel)))) #+end_src Networks to join: #+begin_src emacs-lisp (if (not (eq user-irc-network '"")) (erc :server user-irc-network :port user-irc-port :nick user-nick :password user-irc-password)) #+end_src Enable notifications. #+begin_src emacs-lisp (if (not (eq user-irc-network '"")) (add-to-list 'erc-modules 'notifications)) #+end_src ** json #+begin_src emacs-lisp (defun js-mode-bindings () "Sets a hotkey for using the json-snatcher plugin" (when (string-match "\\.json$" (buffer-name)) (local-set-key (kbd "C-c C-g") 'jsons-print-path))) (add-hook 'js-mode-hook 'js-mode-bindings) (add-hook 'js2-mode-hook 'js-mode-bindings) #+end_src ** Coding *** valgrind (checking for memory leaks) #+begin_src emacs-lisp ; From ; Based on compile.el included with Emacs ; and ideas from ; compile.el is GPL, so this is too. ; FILCAB: I'll make some changes soon... (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 nil (lambda (mode-name) "*valgrind*"))) #+end_src *** Comments (outorg) #+begin_src emacs-lisp (add-hook 'emacs-lisp-mode-hook 'outline-minor-mode) (add-hook 'LaTeX-mode-hook 'outline-minor-mode) (add-hook 'picolisp-mode-hook 'outline-minor-mode) (add-hook 'clojure-mode-hook 'outline-minor-mode) (add-hook 'ess-mode-hook 'outline-minor-mode) (add-hook 'ledger-mode-hook 'outline-minor-mode) (add-hook 'message-mode-hook 'outline-minor-mode) (defvar outline-minor-mode-prefix "\M-#") #+end_src *** Cedet Add further minor-modes to be enabled by semantic-mode. See doc-string of `semantic-default-submodes' for other things you can use here. #+begin_src emacs-lisp (add-to-list 'semantic-default-submodes 'global-semantic-idle-summary-mode nil) (add-to-list 'semantic-default-submodes 'global-semantic-idle-completions-mode nil) (add-to-list 'semantic-default-submodes 'global-cedet-m3-minor-mode nil) #+end_src #+begin_src emacs-lisp (semantic-mode 0) (global-ede-mode 0) #+end_src *** Qt5 #+begin_src emacs-lisp (add-to-list 'auto-mode-alist '("/usr/include/x86_64-linux-gnu/qt5" . c++-mode)) (semantic-add-system-include "/usr/include/x86_64-linux-gnu/qt5" 'c++-mode) ;;(add-to-list 'semantic-lex-c-preprocessor-symbol-file "/usr/include/x86_64-linux-gnu/qt5/Qt/qconfig.h") ;;(add-to-list 'semantic-lex-c-preprocessor-symbol-file "/usr/include/x86_64-linux-gnu/qt5/Qt/qconfig-dist.h") #+end_src *** Rust mode Recognise rust files. #+begin_src emacs-lisp (autoload 'rust-mode "rust-mode" nil t) (add-to-list 'auto-mode-alist '("\\.rs\\'" . rust-mode)) #+end_src Enable flycheck: #+begin_src emacs-lisp (add-hook 'rust-mode-hook 'flycheck-mode) #+end_src Enable the cargo package manager: #+begin_src emacs-lisp (add-hook 'rust-mode-hook 'cargo-minor-mode) #+end_src Indent with C-c #+begin_src emacs-lisp (add-hook 'rust-mode-hook (lambda () (local-set-key (kbd "C-c ") #'rust-format-buffer))) #+end_src *** eshell Some things don't display well in eshell. This can be improved with the following. #+begin_src emacs-lisp (custom-set-variables '(eshell-visual-options (quote (("git" "log" "diff" "show")))) ) #+end_src *** Static analysis #+begin_src emacs-lisp (use-package flycheck-checkbashisms :ensure t :config (flycheck-checkbashisms-setup)) ;; Check 'echo -n' usage (setq flycheck-checkbashisms-newline t) ;; Check non-POSIX issues but required to be supported by Debian Policy 10.4 ;; Setting this variable to non nil made flycheck-checkbashisms-newline effects ;; regardless of its value (setq flycheck-checkbashisms-posix t) (global-flycheck-mode) #+end_src *** let-alist #+BEGIN_SRC emacs-lisp :tangle no ;;; let-alist.el --- Easily let-bind values of an assoc-list by their names -*- lexical-binding: t; -*- ;; Copyright (C) 2014 Free Software Foundation, Inc. ;; Author: Artur Malabarba ;; Maintainer: Artur Malabarba ;; Version: 1.0.3 ;; Keywords: extensions lisp ;; Prefix: let-alist ;; Separator: - ;; This file is part of GNU Emacs. ;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see . ;;; Commentary: ;; This package offers a single macro, `let-alist'. This macro takes a ;; first argument (whose value must be an alist) and a body. ;; ;; The macro expands to a let form containing body, where each dotted ;; symbol inside body is let-bound to their cdrs in the alist. Dotted ;; symbol is any symbol starting with a `.'. Only those present in ;; the body are let-bound and this search is done at compile time. ;; ;; For instance, the following code ;; ;; (let-alist alist ;; (if (and .title .body) ;; .body ;; .site ;; .site.contents)) ;; ;; essentially expands to ;; ;; (let ((.title (cdr (assq 'title alist))) ;; (.body (cdr (assq 'body alist))) ;; (.site (cdr (assq 'site alist))) ;; (.site.contents (cdr (assq 'contents (cdr (assq 'site alist)))))) ;; (if (and .title .body) ;; .body ;; .site ;; .site.contents)) ;; ;; If you nest `let-alist' invocations, the inner one can't access ;; the variables of the outer one. You can, however, access alists ;; inside the original alist by using dots inside the symbol, as ;; displayed in the example above by the `.site.contents'. ;; ;;; Code: (defun let-alist--deep-dot-search (data) "Return alist of symbols inside DATA that start with a `.'. Perform a deep search and return an alist where each car is the symbol, and each cdr is the same symbol without the `.'." (cond ((symbolp data) (let ((name (symbol-name data))) (when (string-match "\\`\\." name) ;; Return the cons cell inside a list, so it can be appended ;; with other results in the clause below. (list (cons data (intern (replace-match "" nil nil name))))))) ((not (listp data)) nil) (t (apply #'append (mapcar #'let-alist--deep-dot-search data))))) (defun let-alist--access-sexp (symbol variable) "Return a sexp used to acess SYMBOL inside VARIABLE." (let* ((clean (let-alist--remove-dot symbol)) (name (symbol-name clean))) (if (string-match "\\`\\." name) clean (let-alist--list-to-sexp (mapcar #'intern (nreverse (split-string name "\\."))) variable)))) (defun let-alist--list-to-sexp (list var) "Turn symbols LIST into recursive calls to `cdr' `assq' on VAR." `(cdr (assq ',(car list) ,(if (cdr list) (let-alist--list-to-sexp (cdr list) var) var)))) (defun let-alist--remove-dot (symbol) "Return SYMBOL, sans an initial dot." (let ((name (symbol-name symbol))) (if (string-match "\\`\\." name) (intern (replace-match "" nil nil name)) symbol))) ;;; The actual macro. ;;;###autoload (defmacro let-alist (alist &rest body) "Let-bind dotted symbols to their cdrs in ALIST and execute BODY. Dotted symbol is any symbol starting with a `.'. Only those present in BODY are let-bound and this search is done at compile time. For instance, the following code (let-alist alist (if (and .title .body) .body .site .site.contents)) essentially expands to (let ((.title (cdr (assq 'title alist))) (.body (cdr (assq 'body alist))) (.site (cdr (assq 'site alist))) (.site.contents (cdr (assq 'contents (cdr (assq 'site alist)))))) (if (and .title .body) .body .site .site.contents)) If you nest `let-alist' invocations, the inner one can't access the variables of the outer one. You can, however, access alists inside the original alist by using dots inside the symbol, as displayed in the example above." (declare (indent 1) (debug t)) (let ((var (make-symbol "alist"))) `(let ((,var ,alist)) (let ,(mapcar (lambda (x) `(,(car x) ,(let-alist--access-sexp (car x) var))) (delete-dups (let-alist--deep-dot-search body))) ,@body)))) ;;;; ChangeLog: ;; 2014-12-22 Artur Malabarba ;; ;; packages/let-alist: Use `make-symbol' instead of `gensym'. ;; ;; 2014-12-20 Artur Malabarba ;; ;; packages/let-alist: Enable access to deeper alists ;; ;; 2014-12-14 Artur Malabarba ;; ;; let-alist.el: Add lexical binding. Version bump. ;; ;; 2014-12-11 Artur Malabarba ;; ;; let-alist: New package ;; (provide 'let-alist) ;;; let-alist.el ends here #+END_SRC *** Kernel programming style See Documentation/CodingStyle #+begin_src emacs-lisp (defun c-lineup-arglist-tabs-only (ignored) "Line up argument lists by tabs, not spaces" (let* ((anchor (c-langelem-pos c-syntactic-element)) (column (c-langelem-2nd-pos c-syntactic-element)) (offset (- (1+ column) anchor)) (steps (floor offset c-basic-offset))) (* (max steps 1) c-basic-offset))) (add-hook 'c-mode-common-hook (lambda () ;; Add kernel style (c-add-style "linux-tabs-only" '("linux" (c-offsets-alist (arglist-cont-nonempty c-lineup-gcc-asm-reg c-lineup-arglist-tabs-only)))))) (add-hook 'c-mode-hook (lambda () (let ((filename (buffer-file-name))) ;; Enable kernel mode for the appropriate files (when (and filename (string-match (expand-file-name "~/develop/kernel") filename)) (setq indent-tabs-mode t) (c-set-style "linux-tabs-only"))))) #+end_src *** php mode #+begin_src emacs-lisp ;;(add-to-list 'load-path "~/elisp/php-mode/") ;;(require 'php-mode) #+end_src *** Javascript mode (js2-mode) #+begin_src emacs-lisp (add-to-list 'load-path "~/elisp/js2-mode/") (require 'js2-mode) #+end_src *** Aggressive indent mode Aggressive indent is often too aggressive, with occasional failures which result in inappropriately indented code. #+begin_src emacs-lisp (add-to-list 'load-path "~/elisp/aggressive-indent-mode/") (require 'aggressive-indent) (add-hook 'emacs-lisp-mode-hook #'aggressive-indent-mode) (add-hook 'css-mode-hook #'aggressive-indent-mode) (global-aggressive-indent-mode 0) ;;(add-to-list 'aggressive-indent-excluded-modes 'html-mode) #+end_src *** Go mode #+begin_src emacs-lisp (add-to-list 'load-path "~/elisp/go-mode/") (require 'go-mode) #+end_src *** Text mode is default for new buffers #+begin_src emacs-lisp (setq default-major-mode 'text-mode) #+end_src *** Remove trailing whitespace #+begin_src emacs-lisp (add-hook 'before-save-hook 'delete-trailing-whitespace) #+end_src *** Shortcut to go to a given line number #+begin_src emacs-lisp (global-set-key "\C-l" 'goto-line) #+end_src *** Show line numbers #+begin_src emacs-lisp (add-hook 'find-file-hook (lambda () (linum-mode 1))) #+end_src *** Scroll with the mouse wheel #+begin_src emacs-lisp (mouse-wheel-mode t) #+end_src *** Indentation using spaces #+begin_src emacs-lisp (setq-default tab-width 4) (setq indent-tabs-mode nil) (setq-default indent-tabs-mode nil) (setq standard-indent 4) (setq c-basic-offset 4) ;; if indent-tabs-mode is off, untabify before saving (add-hook 'write-file-hooks (lambda () (if (not indent-tabs-mode) (untabify (point-min) (point-max))) nil )) #+end_src *** New lines are always indented I almost always want to go to the right indentation on the next line. #+begin_src emacs-lisp (global-set-key (kbd "RET") 'newline-and-indent) #+end_src *** Adapt to being on Windows These config snippets seem to help Windows users. #+begin_src emacs-lisp (add-hook 'comint-output-filter-functions 'shell-strip-ctrl-m nil t) (add-hook 'comint-output-filter-functions 'comint-watch-for-password-prompt nil t) #+end_src *** Expand region Semantically expand the selected text. #+begin_src emacs-lisp (global-set-key (kbd "C-=") 'er/expand-region) #+end_src *** Emacs Lisp **** Edebug Did you know edebug has a trace function? I didn't. Thanks, agumonkey! #+begin_src emacs-lisp (setq edebug-trace t) #+end_src While edebugging, use T to view a trace buffer (=*edebug-trace*=). Emacs will quickly execute the rest of your code, printing out the arguments and return values for each expression it evaluates. **** Eldoc Eldoc provides minibuffer hints when working with Emacs Lisp. #+begin_src emacs-lisp (autoload 'turn-on-eldoc-mode "eldoc" nil t) (add-hook 'emacs-lisp-mode-hook 'turn-on-eldoc-mode) (add-hook 'lisp-interaction-mode-hook 'turn-on-eldoc-mode) (add-hook 'ielm-mode-hook 'turn-on-eldoc-mode) #+end_src **** Refactoring :drill: SCHEDULED: <2013-03-03 Sun> :PROPERTIES: :ID: 99ac7ddb-08ef-46c4-8fa8-8a45164f9e :DRILL_LAST_INTERVAL: 3.86 :DRILL_REPEATS_SINCE_FAIL: 2 :DRILL_TOTAL_REPEATS: 2 :DRILL_FAILURE_COUNT: 1 :DRILL_AVERAGE_QUALITY: 2.5 :DRILL_EASE: 2.36 :DRILL_LAST_QUALITY: 3 :DRILL_LAST_REVIEWED: [2013-02-27 Wed 21:18] :END: More things that I need to get used to... #+begin_src emacs-lisp ;; C-c C-v l : elint current buffer in clean environment. ;; C-c C-v L : elint current buffer by multiple emacs binaries. ;; See `erefactor-lint-emacsen' ;; C-c C-v r : Rename symbol in current buffer. ;; Resolve `let' binding as long as i can. ;; C-c C-v R : Rename symbol in requiring modules and current buffer. ;; C-c C-v h : Highlight current symbol in this buffer ;; and suppress `erefacthr-highlight-mode'. ;; C-c C-v d : Dehighlight all by above command. ;; C-c C-v c : Switch prefill-x bunch of symbols. ;; ex: '(hoge-var hoge-func) -> '(foo-var foo-func) ;; C-c C-v ? : Display flymake elint warnings/errors (use-package erefactor :config (define-key emacs-lisp-mode-map "\C-c\C-v" erefactor-map)) #+end_src **** Jumping to code #+begin_src emacs-lisp (define-key emacs-lisp-mode-map (kbd "C-c .") 'find-function-at-point) (bind-key "C-c f" 'find-function) #+end_src *** Show column number I sometimes need to know where I am in a line. #+begin_src emacs-lisp (column-number-mode 1) #+end_src *** Don't show whitespace in diff, but show context #+begin_src emacs-lisp (setq vc-diff-switches '("-b" "-B" "-u")) #+end_src *** Javascript #+begin_src emacs-lisp (use-package js2-mode :commands js2-mode :init (add-to-list 'auto-mode-alist '("\\.js$" . js2-mode)) :config (progn (bind-key "C-x C-e" 'js-send-last-sexp js2-mode-map) (bind-key "C-M-x" 'js-send-last-sexp-and-go js2-mode-map) (bind-key "C-c b" 'js-send-buffer js2-mode-map) (bind-key "C-c C-b" 'js-send-buffer-and-go js2-mode-map) (bind-key "C-c l" 'js-load-file-and-go js2-mode-map))) #+end_src *** Magit - nice git interface Search the commit log: #+begin_src emacs-lisp :tangle no ;;; search commit log by anything (defvar anything-c-source-log-edit-comment '((name . "Log-edit Comment") (candidates . anything-c-log-edit-comment-candidates) (action . (("Insert" . (lambda (str) (insert str))))) (migemo) (multiline)) "Source for browse and insert Log-edit comment.") (defun anything-c-log-edit-comment-candidates () (let* ((candidates (shell-command-to-string "\\git \\log -500 | \\grep -E '^ .+'")) (logs (string-to-list (split-string candidates "\n ")))) (push (replace-regexp-in-string "^ " "" (pop logs)) logs) logs)) (defun anything-show-log-edit-comment () "`anything' for Log-edit comment." (interactive) (anything-other-buffer 'anything-c-source-log-edit-comment "*anything log-edit comment*")) ;;(define-key magit-log (kbd "C-s") 'anything-show-log-edit-comment) #+end_src #+begin_src emacs-lisp :tangle no (setq magit-auto-revert-mode nil) (setq magit-last-seen-setup-instructions "1.4.0") (defun bashrc/magit-commit-all () "Publish the current file and commit all the current changes." (interactive) (bashrc/org-publish-maybe) (magit-status default-directory) (magit-stage-all) (call-interactively 'magit-log-edit)) (use-package magit :init (setq magit-diff-options '("-b")) ; ignore whitespace :bind (("C-x v d" . magit-status) ("C-x v p" . magit-push) ("C-x v c" . bashrc/magit-commit-all) ("C-x v s" . anything-show-log-edit-comment))) #+end_src If necessary, use an alternative remote instead of 'origin' *** Tag files I don't often use a TAGS file, but when I do, I don't want to have to set my tags file per project. I search for it in the directory tree instead. #+begin_src emacs-lisp (defun bashrc/recursive-find-file (file &optional directory) "Find the first FILE in DIRECTORY or its parents." (setq directory (or directory (file-name-directory (buffer-file-name)) (pwd))) (if (file-exists-p (expand-file-name file directory)) (expand-file-name file directory) (unless (string= directory "/") (bashrc/recursive-find-file file (expand-file-name ".." directory))))) (defun bashrc/find-tags () "Set the TAGS file." (set (make-variable-buffer-local 'tags-table-list) nil) (set (make-variable-buffer-local 'tags-file-name) (bashrc/recursive-find-file "TAGS"))) (eval-after-load 'drupal-mode '(progn (add-hook 'drupal-mode-hook 'bashrc/find-tags))) #+end_src *** Projects #+begin_src emacs-lisp :tangle no (setq projectile-keymap-prefix (kbd "C-c p")) (use-package projectile :init (progn (projectile-global-mode) (setq projectile-completion-system 'default) (setq projectile-enable-caching t))) (use-package helm-projectile) (define-key projectile-mode-map [?\s-d] 'helm-projectile-find-dir) (define-key projectile-mode-map [?\s-p] 'helm-projectile-switch-project) (define-key projectile-mode-map [?\s-f] 'helm-projectile-find-file) (define-key projectile-mode-map [?\s-g] 'help-projectile-grep) #+end_src ** Encryption #+begin_src emacs-lisp :tangle no :eval no (require 'org-crypt) (org-crypt-use-before-save-magic) (setq org-tags-exclude-from-inheritance (quote ("crypt"))) (setq org-crypt-key nil) ;; GPG key to use for encryption ;; Either the Key ID or set to nil to use symmetric encryption. ;; (setq auto-save-default nil) ;; Auto-saving does not cooperate with org-crypt.el: so you need ;; to turn it off if you plan to use org-crypt.el quite often. ;; Otherwise, you'll get an (annoying) message each time you ;; start Org. ;; To turn it off only locally, you can insert this: ;; ;; # -*- buffer-auto-save-file-name: nil; -*- #+end_src Enable encrypt or decrypt using GPG. #+begin_src emacs-lisp (require 'epa) (epa-file-enable) #+end_src ** Advanced stuff / things I tend to forget about *** Editing multiple things **** Multiple cursors mode :drill: I often define keyboard macros to process multiple lines in a region. Maybe =multiple-cursors= will be an even better way. Looks promising! [[][See Emacs Rocks episode 13 (multiple-cursors) for a great demo]]. #+begin_src emacs-lisp (use-package multiple-cursors :bind (("C->" . mc/mark-next-like-this) ("C-<" . mc/mark-previous-like-this) ("C-*" . mc/mark-all-like-this))) #+end_src Thanks to [[][Irreal]] and [[][Planet Emacsen]] for the link! **** All :drill: M-x all lets you edit all lines matching a given regexp. #+begin_src emacs-lisp ;;(use-package all) #+end_src *** Edit list :drill: M-x edit-list makes it easier to edit an Emacs Lisp list. #+begin_src emacs-lisp (use-package edit-list :commands edit-list) #+end_src *** Ace Jump mode :drill: Quickly jump to a position in the current view. #+begin_src emacs-lisp (use-package ace-jump-mode :bind ("M-SPC" . ace-jump-mode)) (bind-key "M-S-SPC" 'just-one-space) #+end_src Ace Window looks useful too. #+begin_src emacs-lisp (use-package ace-window :bind ("M-s a w" . ace-window)) #+end_src *** Network: TRAMP and editing files over SSH Emacs lets you edit files on remote servers, which is pretty darn cool. On Windows, these things help a little. #+begin_src emacs-lisp (setq tramp-default-method "plink") (setq tramp-auto-save-directory "~/temp") #+end_src ** Other nifty Emacs things I want to learn *** Finding the closest Makefile :drill: Look up the directory hierarchy from FILE for a file named NAME. Stop at the first parent directory containing a file NAME, and return the directory. Return nil if not found. **** Answer =find-dominating-file= *** iedit #+begin_src emacs-lisp (use-package iedit) #+end_src *** Smartparens mode :drill: #+begin_src emacs-lisp ;; (bashrc/package-install 'smartparens) (use-package smartparens :init (when (eq system-type 'w32) (require 'smartparens-config) (add-hook 'emacs-lisp-mode-hook 'smartparens-mode) (add-hook 'emacs-lisp-mode-hook 'show-smartparens-mode) ;;;;;;;;;;;;;;;;;;;;;;;; ;; keybinding management (define-key sp-keymap (kbd "C-M-f") 'sp-forward-sexp) (define-key sp-keymap (kbd "C-M-b") 'sp-backward-sexp) (define-key sp-keymap (kbd "C-M-d") 'sp-down-sexp) (define-key sp-keymap (kbd "C-M-a") 'sp-backward-down-sexp) (define-key sp-keymap (kbd "C-S-a") 'sp-beginning-of-sexp) (define-key sp-keymap (kbd "C-S-d") 'sp-end-of-sexp) (define-key sp-keymap (kbd "C-M-e") 'sp-up-sexp) (define-key emacs-lisp-mode-map (kbd ")") 'sp-up-sexp) (define-key sp-keymap (kbd "C-M-u") 'sp-backward-up-sexp) (define-key sp-keymap (kbd "C-M-t") 'sp-transpose-sexp) (define-key sp-keymap (kbd "C-M-n") 'sp-next-sexp) (define-key sp-keymap (kbd "C-M-p") 'sp-previous-sexp) (define-key sp-keymap (kbd "C-M-k") 'sp-kill-sexp) (define-key sp-keymap (kbd "C-M-w") 'sp-copy-sexp) (define-key sp-keymap (kbd "M-") 'sp-unwrap-sexp) (define-key sp-keymap (kbd "M-") 'sp-backward-unwrap-sexp) (define-key sp-keymap (kbd "C-") 'sp-forward-slurp-sexp) (define-key sp-keymap (kbd "C-") 'sp-forward-barf-sexp) (define-key sp-keymap (kbd "C-M-") 'sp-backward-slurp-sexp) (define-key sp-keymap (kbd "C-M-") 'sp-backward-barf-sexp) (define-key sp-keymap (kbd "M-D") 'sp-splice-sexp) (define-key sp-keymap (kbd "C-M-") 'sp-splice-sexp-killing-forward) (define-key sp-keymap (kbd "C-M-") 'sp-splice-sexp-killing-backward) (define-key sp-keymap (kbd "C-S-") 'sp-splice-sexp-killing-around) (define-key sp-keymap (kbd "C-]") 'sp-select-next-thing-exchange) (define-key sp-keymap (kbd "C-") 'sp-select-previous-thing) (define-key sp-keymap (kbd "C-M-]") 'sp-select-next-thing) (define-key sp-keymap (kbd "M-F") 'sp-forward-symbol) (define-key sp-keymap (kbd "M-B") 'sp-backward-symbol) (define-key sp-keymap (kbd "H-t") 'sp-prefix-tag-object) (define-key sp-keymap (kbd "H-p") 'sp-prefix-pair-object) (define-key sp-keymap (kbd "H-s c") 'sp-convolute-sexp) (define-key sp-keymap (kbd "H-s a") 'sp-absorb-sexp) (define-key sp-keymap (kbd "H-s e") 'sp-emit-sexp) (define-key sp-keymap (kbd "H-s p") 'sp-add-to-previous-sexp) (define-key sp-keymap (kbd "H-s n") 'sp-add-to-next-sexp) (define-key sp-keymap (kbd "H-s j") 'sp-join-sexp) (define-key sp-keymap (kbd "H-s s") 'sp-split-sexp) ;;;;;;;;;;;;;;;;;; ;; pair management (sp-local-pair 'minibuffer-inactive-mode "'" nil :actions nil) ;;; markdown-mode (sp-with-modes '(markdown-mode gfm-mode rst-mode) (sp-local-pair "*" "*" :bind "C-*") (sp-local-tag "2" "**" "**") (sp-local-tag "s" "```scheme" "```") (sp-local-tag "<" "<_>" "" :transform 'sp-match-sgml-tags)) ;;; tex-mode latex-mode (sp-with-modes '(tex-mode plain-tex-mode latex-mode) (sp-local-tag "i" "1d5f8e69396c521f645375107197ea4dfbc7b792quot;<" "1d5f8e69396c521f645375107197ea4dfbc7b792quot;>")) ;;; html-mode (sp-with-modes '(html-mode sgml-mode) (sp-local-pair "<" ">")) ;;; lisp modes (sp-with-modes sp--lisp-modes (sp-local-pair "(" nil :bind "C-(")))) #+end_src ** Web browsing Browse within emacs. #+begin_src emacs-lisp (setq browse-url-browser-function 'eww-browse-url) #+end_src ** Transcript editing #+begin_src emacs-lisp (defun bashrc/split-sentence-and-capitalize () (interactive) (delete-char 1) (insert ".") (capitalize-word 1)) (defun bashrc/split-sentence-delete-word-and-capitalize () (interactive) (delete-char 1) (insert ".") (kill-word 1) (capitalize-word 1)) (defun bashrc/delete-word-and-capitalize () (interactive) (skip-syntax-backward "w") (kill-word 1) (capitalize-word 1)) (bind-key "C-c t s" 'bashrc/split-sentence-and-capitalize org-mode-map) (bind-key "C-c t -" 'bashrc/split-sentence-delete-word-and-capitalize org-mode-map) (bind-key "C-c t d" 'bashrc/delete-word-and-capitalize org-mode-map) #+end_src ** Startup #+begin_src emacs-lisp (find-file "~/personal/") (require 'org-compat) (org-agenda nil "a") #+end_src ** Killing buffers Kills unused buffers periodically. #+begin_src emacs-lisp ;;; midnight mode (require 'midnight) ;;kill buffers if they were last disabled more than this seconds ago (setq clean-buffer-list-delay-special 7200) (defvar clean-buffer-list-timer nil "Stores clean-buffer-list timer if there is one. You can disable clean-buffer-list by (cancel-timer clean-buffer-list-timer).") ;; run clean-buffer-list every 2 hours (setq clean-buffer-list-timer (run-at-time t 7200 'clean-buffer-list)) ;; kill everything, clean-buffer-list is very intelligent at not killing ;; unsaved buffer. (setq clean-buffer-list-kill-regexps '("^.*$")) ;; keep these buffer untouched ;; prevent append multiple times (defvar clean-buffer-list-kill-never-buffer-names-init clean-buffer-list-kill-never-buffer-names "Init value for clean-buffer-list-kill-never-buffer-names") (setq clean-buffer-list-kill-never-buffer-names (append '("" "*eshell*" "*Org Agenda*" "*Music*" user-irc-network user-irc-channel) clean-buffer-list-kill-never-buffer-names-init)) ;; prevent append multiple times (defvar clean-buffer-list-kill-never-regexps-init clean-buffer-list-kill-never-regexps "Init value for clean-buffer-list-kill-never-regexps") ;; append to *-init instead of itself (setq clean-buffer-list-kill-never-regexps (append '("^\\*EMMS Playlist\\*.*$") clean-buffer-list-kill-never-regexps-init)) #+end_src * Inactive/infrequent things ** Strike through DONE headlines I wanted a quick way to visually distinguish DONE tasks from tasks I still need to do. This [[][handy snippet from the Emacs Org-mode mailing list]] does the trick by striking through the headlines for DONE tasks. #+begin_src emacs-lisp :tangle no :eval no (setq org-fontify-done-headline t) (custom-set-faces '(org-done ((t (:foreground "PaleGreen" :weight normal :strike-through t)))) '(org-headline-done ((((class color) (min-colors 16) (background dark)) (:foreground "LightSalmon" :strike-through t))))) #+end_src ** Rainbow delimiters #+begin_src emacs-lisp :eval no :tangle no (use-package rainbow-delimiters :init (global-rainbow-delimiters-mode)) #+end_src ** Org - send things to the bottom of the list Handy for collecting items together. #+begin_src emacs-lisp :eval no :tangle no (defun bashrc/org-send-to-bottom-of-list () "Send the current line to the bottom of the list." (interactive) (beginning-of-line) (let ((kill-whole-line t)) (save-excursion (kill-liune 1) (org-end-of-item-list) (yank)))) #+end_src ** Previous weekly review #+begin_src emacs-lisp (defvar bashrc/org-quantified-categories '(("Business" ("Earn" . "Business - Earn") ("E1" . "Business - Earn - Consulting - E1") ("Connect" . "Business - Connect") ("Build" . "Business - Build")) ("Discretionary" ("Social" . "Discretionary - Social") ("Productive" . "Discretionary - Productive") ("Writing" . "Discretionary - Productive - Writing") ("Emacs" . "Discretionary - Productive - Emacs") ("Play" . "Discretionary - Play")) ("Personal" ;("Biking" . "Personal - Bike") ("Routines" . "Personal - Routines")) ("Sleep" nil) ("Unpaid work" ("Commuting" . "Unpaid work - Subway") ("Cook" . "Unpaid work - Cook") ("Tidy" . "Unpaid work - Tidy up"))) "Categories for time summary.") (defun bashrc/org-summarize-time-use (&optional start end) (require 'quantified) (interactive) (unless start (setq start (format-time-string "%Y-%m-%d" (days-to-time (- (time-to-number-of-days base-date) 6))))) (unless end (setq end (format-time-string "%Y-%m-%d" (days-to-time (1+ (time-to-number-of-days base-date)))))) (let ((time-summary (quantified-summarize-time start end)) (categories bashrc/org-quantified-categories) result) (setq result (mapconcat (lambda (a) (if (assoc (car a) time-summary) (concat (format "- %s: %.1f hours" (car a) (/ (cdr (assoc (car a) time-summary)) 3600.0)) (if (cdr a) (let ((detail (delq nil (mapcar (lambda (b) (if (assoc (cdr b) time-summary) (format "%s: %.1f" (car b) (/ (cdr (assoc (cdr b) time-summary)) 3600.0)) nil)) (cdr a))))) (if detail (concat " (" (mapconcat 'identity detail ", ") ")") "")) "") (if (string-equal (car a) "Sleep") (format " - average of %.1f hours per day" (/ (cdr (assoc (car a) time-summary)) 3600.0 7.0)) "") "\n"))) categories "")) (if (called-interactively-p) (insert result) result))) #+end_src List upcoming tasks so that I can see if I'm overloaded. #+begin_src emacs-lisp (defun bashrc/org-summarize-upcoming-week () "Summarize upcoming tasks as a list." (interactive) (org-agenda nil "w") (let ((string (buffer-string)) business relationships life) (with-temp-buffer (insert string) (goto-char (point-min)) (while (re-search-forward bashrc/weekly-review-line-regexp nil t) (cond ((string= (match-string 1) "routines") nil) ; skip routine tasks ((string= (match-string 1) "business") (add-to-list 'business (concat " - [ ] " (match-string 3)))) ((string= (match-string 1) "people") (add-to-list 'relationships (concat " - [ ] " (match-string 3)))) (t (add-to-list 'life (concat " - [ ] " (match-string 3))))))) (setq string (concat "*Plans for next week*\n" "- Business\n" (mapconcat 'identity business "\n") "\n- Relationships\n" (mapconcat 'identity relationships "\n") "\n- Life\n" (mapconcat 'identity life "\n"))) (if (called-interactively-p) (kill-new string) string))) #+end_src This uses Org Agenda's log mode to summarize the tasks that I checked off. I still need to match it up with the plans for the previous week to see which items I'd planned ahead, and which ones were new tasks. (Hmm, is it important to track those separately? I might just skip it.) #+begin_src emacs-lisp (defun bashrc/org-summarize-previous-week () "Summarize previously-completed tasks as a list." (interactive) (save-window-excursion (org-agenda nil "w") (org-agenda-later -1) (org-agenda-log-mode 16) (let ((string (buffer-string)) business relationships life) (with-temp-buffer (insert string) (goto-char (point-min)) (while (re-search-forward bashrc/weekly-review-line-regexp nil t) (cond ((string= (match-string 1) "routines") nil) ; skip routine tasks ((string= (match-string 1) "business") (add-to-list 'business (concat " - " (match-string 2)))) ((string= (match-string 1) "people") (add-to-list 'relationships (concat " - " (match-string 2)))) (t (add-to-list 'life (concat " - " (match-string 2))))))) (setq string (concat "*Accomplished this week*\n\n" "- Business\n" (mapconcat 'identity business "\n") "\n- Relationships\n" (mapconcat 'identity relationships "\n") "\n- Life\n" (mapconcat 'identity life "\n"))) (if (called-interactively-p) (kill-new string) string)))) #+end_src ** Idle timer This snippet is from John Wiegley - It shows the org agenda when Emacs is idle. Thanks to winner-mode, I can get back to my previous buffers with C-c left. #+begin_src emacs-lisp (defun jump-to-org-agenda () (interactive) (let ((buf (get-buffer "*Org Agenda*")) wind) (if buf (if (setq wind (get-buffer-window buf)) (select-window wind) (if (called-interactively-p) (progn (select-window (display-buffer buf t t)) (org-fit-window-to-buffer) ;; (org-agenda-redo) ) (with-selected-window (display-buffer buf) (org-fit-window-to-buffer) ;; (org-agenda-redo) ))) (call-interactively 'org-agenda-list))) ;;(let ((buf (get-buffer "*Calendar*"))) ;; (unless (get-buffer-window buf) ;; (org-agenda-goto-calendar))) ) (run-with-idle-timer 7200 t 'jump-to-org-agenda) #+end_src ** Enable minibuffer completion [2013-03-31] Superseded by ido-hacks? It can be difficult to remember the full names of Emacs commands, so I use =icomplete-mode= for minibuffer completion. This also makes it easier to discover commands. #+begin_src emacs-lisp :eval no :tangle no (icomplete-mode 1) #+end_src ** Work-in-progress: monthly review ... and for good measure, let's do some kind of monthly review too. #+begin_src emacs-lisp :eval no :tangle no (defun bashrc/org-prepare-monthly-review () "Prepare monthly review template." (interactive) (let* ((base-date (org-read-date-analyze "-1m" nil '(0 0 0))) (start (encode-time 0 0 0 1 (elt base-date 4) (elt base-date 5))) (end (encode-time 0 0 0 1 (1+ (elt base-date 4)) (elt base-date 5)))) (save-window-excursion (kill-new (concat "*** Monthly review: " (format-time-string "%B %Y" base-date) " :monthly:\n" "*Blog posts*\n\n" "*Time review*\n" (bashrc/org-summarize-time-use (format-time-string "%Y-%m-%d" start) (format-time-string "%Y-%m-%d" end) (float-time (time-subtract end start)) "\n"))) (yank)))) #+end_src