Skip to content

Latest commit

 

History

History
1507 lines (1223 loc) · 52.5 KB

psmith.org

File metadata and controls

1507 lines (1223 loc) · 52.5 KB

Peter Smith’s emacs configuration file

General configuration

About this file

<<babel-init>>

This is my personal config.

I like using (setq ...) more than Customize because I can neatly organize my configuration that way. Ditto for use-package - I mostly use it to group together package-related config without lots of with-eval-after-load calls, and it also makes declaring keybindings easier.

Starting up

Here’s how we start:

;; This sets up the load path so that we can override it
(package-initialize)
(setq package-enable-at-startup t)

(setq custom-file "~/.emacs.d/custom-settings.el")
(load custom-file t)

Personal information

(setq user-full-name "Peter Smith"
      user-mail-address "[email protected]"
      calendar-latitude -36.848461
      calendar-longitude 174.763336
      calendar-location-name "Auckland, NZ")
                        

Emacs initialization

Add package sources

(unless (assoc-default "melpa" package-archives)
  (add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/") t)
;  (package-refresh-contents)
)

Use M-x package-refresh-contents to reload the list of packages after adding these for the first time.

General configuration

My key bindings

(normal-erase-is-backspace-mode 0)
;
;; Some keys to make things easier
;;
(global-set-key [(shift delete)] 'clipboard-kill-region)
(global-set-key [(control insert)] 'clipboard-kill-ring-save)
(global-set-key [(shift insert)] 'clipboard-yank)
(global-set-key (kbd "C-z") 'undo)

;;quickly open my gtd file - "M-x gtd"
;;
(defun gtd ()
        (interactive)
        (find-file "~/NAS/Org/gtd.org")
        )

Better-defaults

Some of this was taken from https://github.com/technomancy/better-defaults/blob/master/better-defaults.el

If working on two files with the same name, but in different locations, tweak the name to show that.

(require 'uniquify);; These functions are useful. Activate them.
(put 'downcase-region 'disabled nil)
(put 'upcase-region 'disabled nil)
(put 'narrow-to-region 'disabled nil)
(put 'dired-find-alternate-file 'disabled nil)

;; Answering just 'y' or 'n' will do
(defalias 'yes-or-no-p 'y-or-n-p)

;; Keep all backup and auto-save files in one directory
(setq backup-directory-alist '(("." . "~/.emacs.d/backups")))
(setq auto-save-file-name-transforms '((".*" "~/.emacs.d/auto-save-list/" t)))

;; UTF-8 please
(setq locale-coding-system 'utf-8) ; pretty
(set-terminal-coding-system 'utf-8) ; pretty
(set-keyboard-coding-system 'utf-8) ; pretty
(set-selection-coding-system 'utf-8) ; please
(prefer-coding-system 'utf-8) ; with sugar on top
(setq-default indent-tabs-mode nil)

;; Turn off the blinking cursor
(blink-cursor-mode -1)

(setq-default indent-tabs-mode nil)
(setq-default indicate-empty-lines t)

;; Don't count two spaces after a period as the end of a sentence.
;; Just one space is needed.
(setq sentence-end-double-space nil)

;; delete the region when typing, just like as we expect nowadays.
(delete-selection-mode t)

(show-paren-mode t)

(column-number-mode t)

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

(setq uniquify-buffer-name-style 'forward)

;; Don't beep at me
(setq visible-bell t)

;; and lets use ido
(setq ido-enable-flex-matching t)
(setq ido-everywhere t)
(ido-mode 1)

It’s always good to be able to see matching parens

(show-paren-mode 1)

Libraries

;; (use-package dash)

Backups

Disk space is cheap. Save lots.

(setq delete-old-versions t) ;; Ask to delete excess backup versions?
(setq version-control t ;; Use version numbers for backups
    kept-new-versions 16 ;; Number of newest versions to keep
    kept-old-versions 2 ;; Number of oldest versions to keep
)
(setq vc-make-backup-files t)
(setq auto-save-file-name-transforms '((".*" "~/.emacs.d/auto-save-list/" t)))

History

From http://www.wisdomandwonder.com/wp-content/uploads/2014/03/C3F.html:

(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))

Windows configuration

When you’re starting out, 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 that thing off. (Although I changed my mind about the menu - I want that again.) But skip the splash screen

;; (tool-bar-mode -1)
(setq inhibit-splash-screen t)

In my world, sentences end with a single space. This makes sentence navigation commands work for me.

(setq sentence-end-double-space nil)

Change “yes or no” to “y or n”

Lazy people like me never want to type “yes” when “y” will suffice.

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

Help - guide-key

It’s hard to remember keyboard shortcuts. The guide-key package pops up help after a short delay.

(use-package guide-key
  :defer t
  :diminish guide-key-mode
  :config
  (progn
  (setq guide-key/guide-key-sequence '("C-x r" "C-x 4" "C-c"))
  (guide-key-mode 1)))  ; Enable guide-key-mode

UTF-8

(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(prefer-coding-system 'utf-8)

(when (display-graphic-p)
  (setq x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING)))

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.

(use-package windmove
  :bind
  (("<f2> <right>" . windmove-right)
   ("<f2> <left>" . windmove-left)
   ("<f2> <up>" . windmove-up)
   ("<f2> <down>" . windmove-down)
   ))

Recent files

(require 'recentf)
(setq recentf-max-saved-items 200
      recentf-max-menu-items 15)
(recentf-mode)

Coding

Tab width of 2 is compact and readable

(setq-default tab-width 2)

New lines are always indented

I almost always want to go to the right indentation on the next line.

(global-set-key (kbd "RET") 'newline-and-indent)

Line wrapping

I like to have my lines wrapped (albeit visual) around about 75 characters

(add-hook 'markdown-mode-hook 'visual-line-mode)

Fixes for putty

The keyboard is broken when using Putty :(, so lets fix the home and end keys.

;; PuTTY fix. Ugly. Bad. But it works. (Good)
(define-key global-map "\M-[1~" 'beginning-of-line)
(define-key global-map [select] 'end-of-line)

Spelling

Let’s get our spelling rite. This also sets up using mouse-3 instead of mouse-2 for fixing spelling errors; that gets over a problem with text being inserted from the X-windows paste buffer.

(setq ispell-really-hunspell t)
(setq ispell-program-name "hunspell")
(setq ispell-local-dictionary "en_GB")
(setq ispell-local-dictionary-alist
        ;; Please note the list `("-d" "en_US")` contains ACTUAL parameters passed to hunspell
        ;; You could use `("-d" "en_US,en_US-med")` to check with multiple dictionaries
        '(("en_GB" "[[:alpha:]]" "[^[:alpha:]]" "[']" nil ("-d" "en_GB") nil utf-8)
          ))

(autoload 'flyspell-mode "flyspell" "On-the-fly spelling checker." t)
(add-hook 'LaTeX-mode-hook 'flyspell-mode)
(add-hook 'org-mode-hook 'flyspell-mode)
(add-hook 'markdown-mode-hook 'flyspell-mode)

(flyspell-mode 1)

(define-key flyspell-mouse-map [down-mouse-3] #'flyspell-correct-word)

Markdown-mode

Who doesn’t love markdown mode. I know I do. Go for GitHub markdown as necessary.

(use-package markdown-mode
  :ensure t
  :commands (markdown-mode gfm-mode)
  :mode (("README\\.md\\'" . gfm-mode)
         ("\\.md\\'" . markdown-mode)
         ("\\.Rmd\\'" . markdown-mode)
         ("\\.markdown\\'" . markdown-mode))
  :init (setq markdown-command "multimarkdown"))

Some code to render `README.md` more like github when I do `C-c C-c v` (Markdown->Export & View)

(setq markdown-command 
    "pandoc -c file:///home/psmith/NAS/Templates/marked/github-pandoc.css --from markdown_github -t html5 --mathjax --highlight-style pygments --standalone")

eMail with mu4e

(require 'mu4e)

;; these are actually the defaults
(setq
  mu4e-maildir       "/usr/home/psmith/NAS/Backups/Maildir"   ;; top-level Maildir
  mu4e-sent-folder   "/sent"       ;; folder for sent messages
  mu4e-drafts-folder "/drafts"     ;; unfinished messages
  mu4e-trash-folder  "/trash"      ;; trashed messages
  mu4e-refile-folder "/archive")   ;; saved messages


(setq mu4e-attachment-dir  "~/NAS/Downloads")

;;store org-mode links to messages
(require 'org-mu4e)

;;store link to message if in header view, not to header query
(setq org-mu4e-link-query-in-headers-mode nil)

;; And do the right thing in storing and caputuring
(define-key mu4e-headers-mode-map (kbd "C-c c") 'org-mu4e-store-and-capture)
(define-key mu4e-view-mode-map    (kbd "C-c c") 'org-mu4e-store-and-capture)

;; and display images
;; enable inline images
(setq mu4e-view-show-images t)
;; use imagemagick, if available
(when (fboundp 'imagemagick-register-types)
  (imagemagick-register-types))

mu4e and sending email

;; tell message-mode how to send mail
(setq message-send-mail-function 'smtpmail-send-it)

;; Set up the contexts I need

(setq mu4e-contexts
    `( ,(make-mu4e-context
          :name "PeterSmith"
          :enter-func (lambda () (mu4e-message "Switch to the petersmith context"))
          :leave-func (lambda () (mu4e-message "Leaving petersmith context"))
          ;; we match based on the contact-fields of the message
          :match-func (lambda (msg)
                        (when msg 
                          (mu4e-message-contact-field-matches msg 
                            :to "[email protected]")))
          :vars '( ( user-mail-address      . "[email protected]"  )
                   ( user-full-name         . "Peter Smith" )
                   ( mu4e-compose-signature .
                     (concat
                       "Peter Smith\n"
                       "\n"))
										 
                   ;; SMTP configuration
                   ( starttls-use-gnutls . t)
                   ( smtpmail-starttls-credentials
									                    '(("smtp.sendgrid.net" 587 nil nil)))
									 ( smtpmail-auth-credentials (expand-file-name "~/.authinfo.gpg")
                   ( smtpmail-smtp-server . "smtp.sendgrid.net")
                   ( smtpmail-smtp-service . 587)	
									 ))
			,(make-mu4e-context
          :name "66uqs"
          :enter-func (lambda () (mu4e-message "Entering 66uqs context"))
          :leave-func (lambda () (mu4e-message "Leaving 66uqs context"))
          ;; we match based on the contact-fields of the message
          :match-func (lambda (msg)
                        (when msg 
                          (mu4e-message-contact-field-matches msg 
                            :to "[email protected]")))
          :vars '( ( user-mail-address      . "[email protected]"  )
                   ( user-full-name         . "Peter Smith" )
                   ( mu4e-compose-signature .
                     (concat
                       "Peter Smith\n"
                       "\n"))
                   ;; SMTP configuration
                   ( starttls-use-gnutls . t)
                   ( smtpmail-starttls-credentials
									                    '(("smtp.sendgrid.net" 587 nil nil)))
									 ( smtpmail-auth-credentials (expand-file-name "~/.authinfo.gpg")
                   ( smtpmail-smtp-server . "smtp.sendgrid.net")
                   ( smtpmail-smtp-service . 587)	
									 ( smtpmail-debug-info t)))
       ,(make-mu4e-context
          :name "Consulting"
          :enter-func (lambda () (mu4e-message "Switch to the consulting context"))
          ;; no leave-func
          ;; we match based on the contact-fields of the message
          :match-func (lambda (msg)
                        (when msg 
                          (mu4e-message-contact-field-matches msg 
                            :to "[email protected]")))
           :vars '( ( user-mail-address      . "[email protected]"  )
                   ( user-full-name         . "Peter Smith" )
                   ( mu4e-compose-signature .
                     (concat
                       "Peter Smith\n"
                       "\n"))
                   ;; SMTP configuration
                   ( starttls-use-gnutls . t)
                   ( smtpmail-starttls-credentials
									                    '(("smtp.sendgrid.net" 587 nil nil)))
									 ( smtpmail-auth-credentials (expand-file-name "~/.authinfo.gpg")
                   ( smtpmail-smtp-server . "smtp.sendgrid.net")
                   ( smtpmail-smtp-service . 587)	
									 ( smtpmail-debug-info t)
									 ))
			,(make-mu4e-context
          :name "UoA"
          :enter-func (lambda () (mu4e-message "Entering University context"))
          :leave-func (lambda () (mu4e-message "Leaving University context"))
          ;; we match based on the contact-fields of the message
          :match-func (lambda (msg)
                        (when msg 
                          (mu4e-message-contact-field-matches msg 
                            :to "[email protected]")))
          :vars '( ( user-mail-address      . "[email protected]"  )
                   ( user-full-name         . "Peter Smith" )
                   ( mu4e-compose-signature .
                     (concat
                       "Peter Smith\n"
                       "\n"))
                   ;; SMTP configuration
                   ( starttls-use-gnutls . t)
                   ( smtpmail-starttls-credentials
									                    '(("mailhost.auckland.ac.nz" 587 nil nil)))
									 ( smtpmail-auth-credentials (expand-file-name "~/.authinfo.gpg")
                   ( smtpmail-smtp-server . "mailhost.auckland.ac.nz")
                   ( smtpmail-smtp-service . 587)	
									 ( smtpmail-debug-info t)))))
  

  ;; set `mu4e-context-policy` and `mu4e-compose-policy` to tweak when mu4e should
  ;; guess or ask the correct context, e.g.

  ;; start with the first (default) context; 
  ;; default is to ask-if-none (ask when there's no context yet, and none match)
  ;; (setq mu4e-context-policy 'pick-first)

  ;; compose with the current context is no context matches;
  ;; default is to ask 
  ;; (setq mu4e-compose-context-policy nil)

Org-mode

Org-mode! The reason I started using emacs in the first place.

I load `org` in my init.el, to make this type of file possible.

Key bindings

Set up some useful key bindings

(require 'org) ;; just in case
;;
;; Standard key bindings
(global-set-key "\C-cl" 'org-store-link)
(global-set-key "\C-ca" 'org-agenda)
(global-set-key "\C-cb" 'org-iswitchb)
(global-set-key "\C-cc" 'org-capture)
(global-set-key (kbd "<f12>") 'org-agenda)
;;(global-set-key (kbd "<f10>") 'org-archive-subtree-default)

Configurations

Then I set up things so org-mode applies to .org and .org_archive files.

;; Capturing stuff
(setq org-directory "~/NAS/Org")
(setq org-default-notes-file "~/NAS/Org/inbox.org")
(setq org-agenda-files (list "~/NAS/Org" "~/NAS/Org/Calendars"))

(add-to-list 'auto-mode-alist '("\\.\\(org\\|org_archive\\)$" . org-mode))

;; The agenda starts on yesterday for 9 days
(setq org-agenda-start-day "-1d")
(setq org-agenda-span 9)
(setq org-agenda-start-on-weekday nil)
; calendar starts on a Monday
(setq calendar-week-start-day 1)

(setq org-sort-agenda-notime-is-late nil)
(setq org-time-stamp-custom-formats (quote ("<%d/%m/%y %a>" . "<%d/%m/%y %a %H:%M>")))

;; Always hilight the current agenda line
(add-hook 'org-agenda-mode-hook
          '(lambda () (hl-line-mode 1))
          'append)

;; Automatically reload buffers if the file on disk changes
(add-hook 'org-mode-hook 'auto-revert-mode)

Refiling

; Targets include this file and any file contributing to the agenda - up to 4 levels deep
(setq org-refile-targets (quote ((nil :maxlevel . 4)
                                 (org-agenda-files :maxlevel . 4))))

; Use full outline paths for refile targets - we file directly with IDO
(setq org-refile-use-outline-path t)

; Targets complete directly with IDO
(setq org-outline-path-complete-in-steps nil)

Tasks and states

I use one set of TODO keywords for all of my org files. Org-mode lets you define TODO keywords per file but I find it’s easier to have a standard set of TODO keywords globally so I can use the same setup in any org file I’m working with.

The only exception to this is this document; since I don’t want org-mode hiding the TODO keyword when it appears in headlines. I’ve set up a dummy #+SEQ_TODO: FIXME FIXED entry at the top of this file just to leave my TODO keyword untouched in this document.

key words

(setq org-todo-keywords
      (quote ((sequence "TODO(t)" "NEXT(n)" "|" "DONE(d)")
              (sequence "WAITING(w@/!)" "HOLD(h@/!)" "|" "CANCELLED(c@/!)" "PHONE"))))

(setq org-todo-keyword-faces
      (quote (("TODO" :foreground "red" :weight bold)
              ("NEXT" :foreground "blue" :weight bold)
              ("DONE" :foreground "forest green" :weight bold)
              ("WAITING" :foreground "orange" :weight bold)
              ("HOLD" :foreground "magenta" :weight bold)
              ("CANCELLED" :foreground "forest green" :weight bold)
              ("PHONE" :foreground "forest green" :weight bold))))

Fast Todo Selection

Fast todo selection allows changing from any task todo state to any other state directly by selecting the appropriate key from the fast todo selection key menu. This is a great feature!

Changing a task state is done with C-c C-t KEY

where KEY is the appropriate fast todo state selection key as defined in org-todo-keywords.

The setting allows changing todo states with S-left and S-right skipping all of the normal processing when entering or leaving a todo state. This cycles through the todo states but skips setting timestamps and entering notes which is very convenient when all you want to do is fix up the status of an entry.

(setq org-use-fast-todo-selection t)

(setq org-treat-S-cursor-todo-selection-as-state-change nil)

Stuck projects

So this is for use with org agenda, stuck project *C-c a #*

Dead projects, those that should not be considered stuck are any of:

  • Cancelled projects
  • Cancelled papers
  • Someday
(setq org-stuck-projects
      '("+PROJECT-CANCELLED|+PAPER-CANCELLED|+SOMEDAY" ("NEXT" "TODO" "DONE" "") ("@Errands")
        "\\<IGNORE\\>"))

Agenda views

Try and get everything I need on to one page.

;; Do not dim blocked tasks
(setq org-agenda-dim-blocked-tasks nil)

;; Compact the block agenda view
(setq org-agenda-compact-blocks t)

;; Custom agenda command definitions

(setq org-agenda-custom-commands
      (quote (("N" "Notes" tags "NOTE"
               ((org-agenda-overriding-header "Notes")
                (org-tags-match-list-sublevels t)))
              (" " "Agenda"
               ((agenda "" nil)
                (tags "REFILE"
                      ((org-agenda-overriding-header "Tasks to Refile")
                       (org-tags-match-list-sublevels nil)))
                (tags-todo "-CANCELLED/!"
                           ((org-agenda-overriding-header "Stuck Projects")
                            (org-agenda-skip-function 'bh/skip-non-stuck-projects)
                            (org-agenda-sorting-strategy
                             '(category-keep))))
                (tags-todo "-HOLD-CANCELLED/!"
                           ((org-agenda-overriding-header "Projects")
                            (org-agenda-skip-function 'bh/skip-non-projects)
                            (org-tags-match-list-sublevels 'indented)
                            (org-agenda-sorting-strategy
                             '(category-keep))))
                (tags-todo "-CANCELLED/!NEXT"
                           ((org-agenda-overriding-header (concat "Project Next Tasks"
                                                                  (if bh/hide-scheduled-and-waiting-next-tasks
                                                                      ""
                                                                    " (including WAITING and SCHEDULED tasks)")))
                            (org-agenda-skip-function 'bh/skip-projects-and-habits-and-single-tasks)
                            (org-tags-match-list-sublevels t)
                            (org-agenda-todo-ignore-scheduled bh/hide-scheduled-and-waiting-next-tasks)
                            (org-agenda-todo-ignore-deadlines bh/hide-scheduled-and-waiting-next-tasks)
                            (org-agenda-todo-ignore-with-date bh/hide-scheduled-and-waiting-next-tasks)
                            (org-agenda-sorting-strategy
                             '(todo-state-down effort-up category-keep))))
                (tags-todo "-REFILE-CANCELLED-WAITING-HOLD/!"
                           ((org-agenda-overriding-header (concat "Project Subtasks"
                                                                  (if bh/hide-scheduled-and-waiting-next-tasks
                                                                      ""
                                                                    " (including WAITING and SCHEDULED tasks)")))
                            (org-agenda-skip-function 'bh/skip-non-project-tasks)
                            (org-agenda-todo-ignore-scheduled bh/hide-scheduled-and-waiting-next-tasks)
                            (org-agenda-todo-ignore-deadlines bh/hide-scheduled-and-waiting-next-tasks)
                            (org-agenda-todo-ignore-with-date bh/hide-scheduled-and-waiting-next-tasks)
                            (org-agenda-sorting-strategy
                             '(category-keep))))
                (tags-todo "-REFILE-CANCELLED-WAITING-HOLD/!"
                           ((org-agenda-overriding-header (concat "Standalone Tasks"
                                                                  (if bh/hide-scheduled-and-waiting-next-tasks
                                                                      ""
                                                                    " (including WAITING and SCHEDULED tasks)")))
                            (org-agenda-skip-function 'bh/skip-project-tasks)
                            (org-agenda-todo-ignore-scheduled bh/hide-scheduled-and-waiting-next-tasks)
                            (org-agenda-todo-ignore-deadlines bh/hide-scheduled-and-waiting-next-tasks)
                            (org-agenda-todo-ignore-with-date bh/hide-scheduled-and-waiting-next-tasks)
                            (org-agenda-sorting-strategy
                             '(category-keep))))
                (tags-todo "-CANCELLED+WAITING|HOLD/!"
                           ((org-agenda-overriding-header (concat "Waiting and Postponed Tasks"
                                                                  (if bh/hide-scheduled-and-waiting-next-tasks
                                                                      ""
                                                                    " (including WAITING and SCHEDULED tasks)")))
                            (org-agenda-skip-function 'bh/skip-non-tasks)
                            (org-tags-match-list-sublevels nil)
                            (org-agenda-todo-ignore-scheduled bh/hide-scheduled-and-waiting-next-tasks)
                            (org-agenda-todo-ignore-deadlines bh/hide-scheduled-and-waiting-next-tasks)))
                (tags "-REFILE/"
                      ((org-agenda-overriding-header "Tasks to Archive")
                       (org-agenda-skip-function 'bh/skip-non-archivable-tasks)
                       (org-tags-match-list-sublevels nil))))
               nil))))

And then some code to handle the heavy lifting with agenda views.

(defun bh/skip-non-archivable-tasks ()
  "Skip trees that are not available for archiving"
  (save-restriction
    (widen)
    ;; Consider only tasks with done todo headings as archivable candidates
    (let ((next-headline (save-excursion (or (outline-next-heading) (point-max))))
          (subtree-end (save-excursion (org-end-of-subtree t))))
      (if (member (org-get-todo-state) org-todo-keywords-1)
          (if (member (org-get-todo-state) org-done-keywords)
              (let* ((daynr (string-to-number (format-time-string "%d" (current-time))))
                     (a-month-ago (* 60 60 24 (+ daynr 1)))
                     (last-month (format-time-string "%Y-%m-" (time-subtract (current-time) (seconds-to-time a-month-ago))))
                     (this-month (format-time-string "%Y-%m-" (current-time)))
                     (subtree-is-current (save-excursion
                                           (forward-line 1)
                                           (and (< (point) subtree-end)
                                                (re-search-forward (concat last-month "\\|" this-month) subtree-end t)))))
                (if subtree-is-current
                    subtree-end ; Has a date in this month or last month, skip it
                  nil))  ; available to archive
            (or subtree-end (point-max)))
        next-headline))))

(defun bh/is-project-p ()
  "Any task with a todo keyword subtask"
  (save-restriction
    (widen)
    (let ((has-subtask)
          (subtree-end (save-excursion (org-end-of-subtree t)))
          (is-a-task (member (nth 2 (org-heading-components)) org-todo-keywords-1)))
      (save-excursion
        (forward-line 1)
        (while (and (not has-subtask)
                    (< (point) subtree-end)
                    (re-search-forward "^\*+ " subtree-end t))
          (when (member (org-get-todo-state) org-todo-keywords-1)
            (setq has-subtask t))))
      (and is-a-task has-subtask))))

(defun bh/is-project-subtree-p ()
  "Any task with a todo keyword that is in a project subtree.
Callers of this function already widen the buffer view."
  (let ((task (save-excursion (org-back-to-heading 'invisible-ok)
                              (point))))
    (save-excursion
      (bh/find-project-task)
      (if (equal (point) task)
          nil
        t))))

(defun bh/is-task-p ()
  "Any task with a todo keyword and no subtask"
  (save-restriction
    (widen)
    (let ((has-subtask)
          (subtree-end (save-excursion (org-end-of-subtree t)))
          (is-a-task (member (nth 2 (org-heading-components)) org-todo-keywords-1)))
      (save-excursion
        (forward-line 1)
        (while (and (not has-subtask)
                    (< (point) subtree-end)
                    (re-search-forward "^\*+ " subtree-end t))
          (when (member (org-get-todo-state) org-todo-keywords-1)
            (setq has-subtask t))))
      (and is-a-task (not has-subtask)))))

(defun bh/is-subproject-p ()
  "Any task which is a subtask of another project"
  (let ((is-subproject)
        (is-a-task (member (nth 2 (org-heading-components)) org-todo-keywords-1)))
    (save-excursion
      (while (and (not is-subproject) (org-up-heading-safe))
        (when (member (nth 2 (org-heading-components)) org-todo-keywords-1)
          (setq is-subproject t))))
    (and is-a-task is-subproject)))

(defun bh/list-sublevels-for-projects-indented ()
  "Set org-tags-match-list-sublevels so when restricted to a subtree we list all subtasks.
  This is normally used by skipping functions where this variable is already local to the agenda."
  (if (marker-buffer org-agenda-restrict-begin)
      (setq org-tags-match-list-sublevels 'indented)
    (setq org-tags-match-list-sublevels nil))
  nil)

(defun bh/list-sublevels-for-projects ()
  "Set org-tags-match-list-sublevels so when restricted to a subtree we list all subtasks.
  This is normally used by skipping functions where this variable is already local to the agenda."
  (if (marker-buffer org-agenda-restrict-begin)
      (setq org-tags-match-list-sublevels t)
    (setq org-tags-match-list-sublevels nil))
  nil)

(defvar bh/hide-scheduled-and-waiting-next-tasks t)

(defun bh/toggle-next-task-display ()
  (interactive)
  (setq bh/hide-scheduled-and-waiting-next-tasks (not bh/hide-scheduled-and-waiting-next-tasks))
  (when  (equal major-mode 'org-agenda-mode)
    (org-agenda-redo))
  (message "%s WAITING and SCHEDULED NEXT Tasks" (if bh/hide-scheduled-and-waiting-next-tasks "Hide" "Show")))

(defun bh/skip-stuck-projects ()
  "Skip trees that are not stuck projects"
  (save-restriction
    (widen)
    (let ((next-headline (save-excursion (or (outline-next-heading) (point-max)))))
      (if (bh/is-project-p)
          (let* ((subtree-end (save-excursion (org-end-of-subtree t)))
                 (has-next ))
            (save-excursion
              (forward-line 1)
              (while (and (not has-next) (< (point) subtree-end) (re-search-forward "^\\*+ NEXT " subtree-end t))
                (unless (member "WAITING" (org-get-tags-at))
                  (setq has-next t))))
            (if has-next
                nil
              next-headline)) ; a stuck project, has subtasks but no next task
        nil))))

(defun bh/skip-non-stuck-projects ()
  "Skip trees that are not stuck projects"
  ;; (bh/list-sublevels-for-projects-indented)
  (save-restriction
    (widen)
    (let ((next-headline (save-excursion (or (outline-next-heading) (point-max)))))
      (if (bh/is-project-p)
          (let* ((subtree-end (save-excursion (org-end-of-subtree t)))
                 (has-next ))
            (save-excursion
              (forward-line 1)
              (while (and (not has-next) (< (point) subtree-end) (re-search-forward "^\\*+ NEXT " subtree-end t))
                (unless (member "WAITING" (org-get-tags-at))
                  (setq has-next t))))
            (if has-next
                next-headline
              nil)) ; a stuck project, has subtasks but no next task
        next-headline))))

(defun bh/skip-non-projects ()
  "Skip trees that are not projects"
  ;; (bh/list-sublevels-for-projects-indented)
  (if (save-excursion (bh/skip-non-stuck-projects))
      (save-restriction
        (widen)
        (let ((subtree-end (save-excursion (org-end-of-subtree t))))
          (cond
           ((bh/is-project-p)
            nil)
           ((and (bh/is-project-subtree-p) (not (bh/is-task-p)))
            nil)
           (t
            subtree-end))))
    (save-excursion (org-end-of-subtree t))))

(defun bh/skip-non-tasks ()
  "Show non-project tasks.
Skip project and sub-project tasks, habits, and project related tasks."
  (save-restriction
    (widen)
    (let ((next-headline (save-excursion (or (outline-next-heading) (point-max)))))
      (cond
       ((bh/is-task-p)
        nil)
       (t
        next-headline)))))

(defun bh/skip-project-trees-and-habits ()
  "Skip trees that are projects"
  (save-restriction
    (widen)
    (let ((subtree-end (save-excursion (org-end-of-subtree t))))
      (cond
       ((bh/is-project-p)
        subtree-end)
       (t
        nil)))))

(defun bh/skip-projects-and-habits-and-single-tasks ()
  "Skip trees that are projects, tasks that are habits, single non-project tasks"
  (save-restriction
    (widen)
    (let ((next-headline (save-excursion (or (outline-next-heading) (point-max)))))
      (cond
       ((and bh/hide-scheduled-and-waiting-next-tasks
             (member "WAITING" (org-get-tags-at)))
        next-headline)
       ((bh/is-project-p)
        next-headline)
       ((and (bh/is-task-p) (not (bh/is-project-subtree-p)))
        next-headline)
       (t
        nil)))))

(defun bh/skip-project-tasks-maybe ()
  "Show tasks related to the current restriction.
When restricted to a project, skip project and sub project tasks, habits, NEXT tasks, and loose tasks.
When not restricted, skip project and sub-project tasks, habits, and project related tasks."
  (save-restriction
    (widen)
    (let* ((subtree-end (save-excursion (org-end-of-subtree t)))
           (next-headline (save-excursion (or (outline-next-heading) (point-max))))
           (limit-to-project (marker-buffer org-agenda-restrict-begin)))
      (cond
       ((bh/is-project-p)
        next-headline)
       ((and (not limit-to-project)
             (bh/is-project-subtree-p))
        subtree-end)
       ((and limit-to-project
             (bh/is-project-subtree-p)
             (member (org-get-todo-state) (list "NEXT")))
        subtree-end)
       (t
        nil)))))

(defun bh/skip-project-tasks ()
  "Show non-project tasks.
Skip project and sub-project tasks, habits, and project related tasks."
  (save-restriction
    (widen)
    (let* ((subtree-end (save-excursion (org-end-of-subtree t))))
      (cond
       ((bh/is-project-p)
        subtree-end)
       ((bh/is-project-subtree-p)
        subtree-end)
       (t
        nil)))))

(defun bh/skip-non-project-tasks ()
  "Show project tasks.
Skip project and sub-project tasks, habits, and loose non-project tasks."
  (save-restriction
    (widen)
    (let* ((subtree-end (save-excursion (org-end-of-subtree t)))
           (next-headline (save-excursion (or (outline-next-heading) (point-max)))))
      (cond
       ((bh/is-project-p)
        next-headline)
       ((and (bh/is-project-subtree-p)
             (member (org-get-todo-state) (list "NEXT")))
        subtree-end)
       ((not (bh/is-project-subtree-p))
        subtree-end)
       (t
        nil)))))

(defun bh/skip-projects-and-habits ()
  "Skip trees that are projects and tasks that are habits"
  (save-restriction
    (widen)
    (let ((subtree-end (save-excursion (org-end-of-subtree t))))
      (cond
       ((bh/is-project-p)
        subtree-end)
       (t
        nil)))))

(defun bh/skip-non-subprojects ()
  "Skip trees that are not projects"
  (let ((next-headline (save-excursion (outline-next-heading))))
    (if (bh/is-subproject-p)
        nil
      next-headline)))

(defun bh/find-project-task ()
  "Move point to the parent (project) task if any"
  (save-restriction
    (widen)
    (let ((parent-task (save-excursion (org-back-to-heading 'invisible-ok) (point))))
      (while (org-up-heading-safe)
        (when (member (nth 2 (org-heading-components)) org-todo-keywords-1)
          (setq parent-task (point))))
      (goto-char parent-task)
      parent-task)))

Other misc settings

(setq org-remember-templates
      '(("Todo" ?t "* TODO %?\n  %i\n  %a" "~/NAS/Org/gtd.org" "Tasks")
        ("Journal" ?j "* %U %?\n\n  %i\n  %a" "~/NAS/Org/journal.org")
        ("Idea" ?i "* %^{Title}\n  %i\n  %a" "~/NAS/Org/journal.org" "New Ideas")))



(setq org-todo-state-tags-triggers
      (quote (("CANCELLED" ("CANCELLED" . t))
              ("WAITING" ("WAITING" . t))
              ("HOLD" ("WAITING" . t) ("HOLD" . t))
              (done ("WAITING") ("HOLD"))
              ("TODO" ("WAITING") ("CANCELLED") ("HOLD"))
              ("NEXT" ("WAITING") ("CANCELLED") ("HOLD"))
              ("DONE" ("WAITING") ("CANCELLED") ("HOLD")))))

;; Org-bibtex
;;	
;; Adding a line like the following is enough to tell org-mode which .bib file to consult:
;;
;; #+BIBLIOGRAPHY: refs plain
;; refs is the name of the BibTeX file (.bib extension omitted), and plain is the bibliographystyle.
;;
;; You can then call org-reftex-citation (bound to C-c C-x [ by default) to insert references.
;; 
;; From the documentation:
;; 
;; (org-reftex-citation)
;; 
;; Use reftex-citation to insert a citation into the buffer. This looks for a line like
;; 
;; #+BIBLIOGRAPHY: foo plain option:-d
;; 
;; and derives from it that foo.bib is the bibliography file relevant for this document. It then installs the necessary environment for RefTeX to work in this buffer and calls reftex-citation to insert a citation into the buffer.
;; 
;; Export of such citations to both LaTeX and HTML is handled by the contributed package ox-bibtex by Taru Karttunen.

(require 'org)
(require 'ox-bibtex)
;;

;; Enable the export to Markdown
(eval-after-load "org"
  '(require 'ox-md nil t))

Adding new tasks quickly

Org Capture mode replaces remember mode for capturing tasks and notes.

To add new tasks efficiently I use a minimal number of capture templates. I used to have lots of capture templates, one for each org-file. I’d start org-capture with C-c c and then pick a template that filed the task under * Tasks in the appropriate file.

I found I still needed to refile these capture tasks again to the correct location within the org-file so all of these different capture templates weren’t really helping at all. Since then I’ve changed my workflow to use a minimal number of capture templates – I create the new task quickly and refile it once. This also saves me from maintaining my org-capture templates when I add a new org

;; Capture templates for: TODO tasks, Notes, appointments, phone calls, and org-protocol
(setq org-capture-templates
      (quote (("t" "todo" entry (file "~/NAS/Org/inbox.org")
							 "* TODO [#A] %?\nSCHEDULED: %(org-insert-time-stamp (org-read-date nil t \"+2d\"))\n %i\n %a")
              ("r" "respond" entry (file "~/NAS/Org/inbox.org")
               "* NEXT Respond to %:from on %:subject\nSCHEDULED: %t\n%U\n%a\n")
              ("n" "note" entry (file "~/NAS/Org/inbox.org")
               "* %? :NOTE:\n%U\n%a\n")
              ("j" "Journal/Blog" entry (file+datetree "~/NAS/Org/journal.org")
               "* %?\n%U\n")
              ("w" "org-protocol" entry (file "~/NAS/Org/inbox.org")
               "* TODO Review %c\n%U\n")
              ("p" "Phone call" entry (file "~/NAS/Org/inbox.org")
               "* PHONE %? :PHONE:\n%U")
              ("h" "Habit" entry (file "~/NAS/Org/inbox.org")
               "* NEXT %?\n%U\n%a\nSCHEDULED: %(format-time-string \"<%Y-%m-%d %a .+1d/3d>\")\n:PROPERTIES:\n:STYLE: habit\n:REPEAT_TO_STATE: NEXT\n:END:\n"))))

Archiving

(setq org-archive-mark-done nil)
(setq org-archive-location "%s_archive::* Archived Tasks")

Sort out blank lines nicely

;;;###autoload
(defun ps/org-fix-blank-lines (prefix)
  "Ensure that blank lines exist between headings and between headings and their contents.
With prefix, operate on whole buffer. Ensures that blank lines
exist after each headings's drawers."
  (interactive "P")
  (org-map-entries (lambda ()
                     (org-with-wide-buffer
                      ;; `org-map-entries' narrows the buffer, which prevents us from seeing
                      ;; newlines before the current heading, so we do this part widened.
                      (while (not (looking-back "\n\n" nil))
                        ;; Insert blank lines before heading.
                        (insert "\n")))
                     (let ((end (org-entry-end-position)))
                       ;; Insert blank lines before entry content
                       (forward-line)
                       (while (and (org-at-planning-p)
                                   (< (point) (point-max)))
                         ;; Skip planning lines
                         (forward-line))
                       (while (re-search-forward org-drawer-regexp end t)
                         ;; Skip drawers. You might think that `org-at-drawer-p' would suffice, but
                         ;; for some reason it doesn't work correctly when operating on hidden text.
                         ;; This works, taken from `org-agenda-get-some-entry-text'.
                         (re-search-forward "^[ \t]*:END:.*\n?" end t)
                         (goto-char (match-end 0)))
                       (unless (or (= (point) (point-max))
                                   (org-at-heading-p)
                                   (looking-at-p "\n"))
                         (insert "\n"))))
                   t (if prefix
                         nil
                       'tree)))

Allow sunrise and sunset seperately in the calendar

I like to know if it’s going to be day or night (well if there will be daylight) when I do stuff.

These functions allow me to add this to may calendar.org

* Sunrise and Sunset
%%(diary-sunrise)
%%(diary-sunset)
(require 'solar)

;; Sunrise (edits by Eph Zero)
;; Brady Trainor
;; http://stackoverflow.com/questions/22889036/custom-diary-sunrise-function-not-working-autoload-diary-emacs

(defun solar-sunrise-string (date &optional nolocation)
  "String of *local* time of sunrise and daylight on Gregorian DATE."
  (let ((l (solar-sunrise-sunset date)))
    (format
     "%s (%s hours daylight)"
     (if (car l)
     (concat "Sunrise " (apply 'solar-time-string (car l)))
       "no sunrise")
     (nth 2 l)
     )))
;; To be called from diary-list-sexp-entries, where DATE is bound.
;;;###diary-autoload
(defun diary-sunrise ()
  "Local time of sunrise as a diary entry.
  Accurate to a few seconds."
  (or (and calendar-latitude calendar-longitude calendar-time-zone)
      (solar-setup))
  (solar-sunrise-string date))


  ;; Sunset
  ;; Brady Trainor
  ;; http://stackoverflow.com/questions/22889036/custom-diary-sunrise-function-not-working-autoload-diary-emacs

  (defun solar-sunset-string (date &optional nolocation)
  "String of *local* time of sunset and daylight on Gregorian DATE."
  (let ((l (solar-sunrise-sunset date)))
    (format
     "%s (%s hours daylight)"
     (if (cadr l)
     (concat "Sunset " (apply 'solar-time-string (cadr l)))
       "no sunset")
     (nth 2 l)
     )))
;; To be called from diary-list-sexp-entries, where DATE is bound.
;;;###diary-autoload
(defun diary-sunset ()
  "Local time of sunset as a diary entry.
  Accurate to a few seconds."
  (or (and calendar-latitude calendar-longitude calendar-time-zone)
      (solar-setup))
  (solar-sunset-string date))

  (provide 'sunrise-sunset)

APA6 styled exporting

So C-c C-e l p will export the current org file to a PDF. This little bit of magic will format stuff as APA6 headings etc. The org documnet needs some kind of heading like this:

(add-to-list 'org-latex-classes
'("apa6"
"\\documentclass[doc,noextraspace,11pt,a4paper]{apa6}
\\usepackage[T1]{fontenc}
\\usepackage[british]{babel}
\\usepackage[hyphens]{url}
\\usepackage{amsmath}
\\usepackage{csquotes}
\\usepackage{graphicx}
\\usepackage{hyperref}
\\usepackage{enumitem}
\\usepackage{mathpazo}
\\setlist[enumerate,itemize]{noitemsep,nolistsep,leftmargin=*}

\\usepackage[backend=biber,style=apa]{biblatex}
\\DeclareLanguageMapping{british}{british-apa}
\\addbibresource{/home/psmith/NAS/Work/Research/Citations/bibs/psmithLibrary.bib}
[NO-DEFAULT-PACKAGES]
[NO-PACKAGES]"
("\\section{%s}" . "\\section*{%s}")
("\\subsection{%s}" . "\\subsection*{%s}")
("\\subsubsection{%s}" . "\\subsubsection*{%s}")
))

Hugo mode

(setq hugo-base-dir "~/NAS/Programming/Websites-source/petersmith/"
      hugo-buffer "*hugo*")

(defun hugo-new-post ()
  (interactive)
  (let* ((title (read-from-minibuffer "Title: "))
         (filename (concat "blog/" 
                           (read-from-minibuffer "Filename: "
                                                 (replace-regexp-in-string "-\\.md" ".md"
                                                   (concat (today-is) (downcase
                                                            (replace-regexp-in-string "[^a-z0-9]+" "-"
                                                                                      title))
                                                           ".md")))))
         (path (concat hugo-base-dir "content/" filename)))
    (if (file-exists-p path)
        (message "File already exists!")
		  (hugo-command "new" filename)
      (find-file path)
      (hugo-replace-key "title" title)
      (goto-char (point-max))
			(save-buffer))))

(defun today-is ()
  (format-time-string "%Y%m%d-"))

(defun hugo-command (&rest args)
  (let ((default-directory (expand-file-name hugo-base-dir)))
    (apply 'call-process "hugo" nil hugo-buffer t args)))

(defun hugo-replace-key (key val)
  (save-excursion
    (goto-char (point-min))
    ; quoted value
    (if (and (re-search-forward (concat key " = \"") nil t)
               (re-search-forward "[^\"]+" (line-end-position) t))
        (or (replace-match val) t) ; ensure we return t
      ; unquoted value
      (when (and (re-search-forward (concat key " = ") nil t)
                 (re-search-forward ".+" (line-end-position) t))
        (or (replace-match val) t)))))


(defun hugo-publish ()
  (interactive)
  (let* ((default-directory (concat (expand-file-name hugo-base-dir) "/")))
    (when (call-process "bash" nil hugo-buffer t  "./Scripts/deploySite.sh")
      (message "Blog published"))))

Reftex

I need references … they are automagically exported from Zotero to my .bib

This hooks in reftex and allows pandoc/markdown formatted citations to be inserted.

(require 'reftex)

(setq reftex-default-bibliography '("~/NAS/Work/Research/Citations/bibs/psmithLibrary.bib"))
(setq reftex-bibliography-commands '("bibliography" "nobibliography" "addbibresource"))

(add-hook 'LaTeX-mode-hook 'turn-on-reftex)   ; with AUCTeX LaTeX mode
(add-hook 'latex-mode-hook 'turn-on-reftex)   ; with Emacs latex mode

(autoload 'reftex-mode     "reftex" "RefTeX Minor Mode" t)
(autoload 'turn-on-reftex  "reftex" "RefTeX Minor Mode" nil)
(autoload 'reftex-citation "reftex-cite" "Make citation" nil)
(autoload 'reftex-index-phrase-mode "reftex-index" "Phrase mode" t)

;; Make RefTeX faster
(setq reftex-enable-partial-scans t)
(setq reftex-save-parse-info t)
(setq reftex-use-multiple-selection-buffers t)
(setq reftex-plug-into-AUCTeX t)



;; define markdown citation formats
(defvar markdown-cite-format)
(setq markdown-cite-format
      '(
        (?\C-m . "[@%l]")
        (?p . "[@%l]")
        (?t . "@%l")
        )
      )

;; wrap reftex-citation with local variables for markdown format
(defun markdown-reftex-citation ()
  (interactive)
  (let ((reftex-cite-format markdown-cite-format)
        (reftex-cite-key-separator "; @"))
    (reftex-citation)))


;; bind modified reftex-citation to C-c[, without enabling reftex-mode
;; https://www.gnu.org/software/auctex/manual/reftex/Citations-Outside-LaTeX.html#SEC31
(add-hook
 'markdown-mode-hook
 (lambda ()
   (define-key markdown-mode-map "\C-c[" 'markdown-reftex-citation)))



;; Make RefTeX work with Org-Mode
;; use 'C-c (' instead of 'C-c [' because the latter is already
;; defined in orgmode to the add-to-agenda command.
(defun org-mode-reftex-setup ()
  (load-library "reftex") 
  (and (buffer-file-name)
  (file-exists-p (buffer-file-name))
  (reftex-parse-all))
  (define-key org-mode-map (kbd "C-c (") 'reftex-citation))

(add-hook 'org-mode-hook 'org-mode-reftex-setup)

;; RefTeX formats for biblatex (not natbib)
(setq reftex-cite-format
      '(
        (?\C-m . "\\autocite[]{%l}")
        (?t . "\\textcite{%l}")
        (?c . "\\cite[]{%l}")
        (?e . "\\autocite[e.g.,][]{%l}")
        (?s . "\\autocite[see]{%l}")
        (?a . "\\citeauthor{%l}")
        (?y . "\\citeyear{%l}")

        (?p . "\\parencite{%l}")
        (?f . "\\footcite[][]{%l}")
        (?F . "\\fullcite[]{%l}")
        (?x . "[]{%l}")
        (?X . "{%l}")
        ))

(setq font-latex-match-reference-keywords
      '(("cite" "[{")
        ("cites" "[{}]")
        ("autocite" "[{")
        ("footcite" "[{")
        ("footcites" "[{")
        ("parencite" "[{")
        ("textcite" "[{")
        ("fullcite" "[{") 
        ("citetitle" "[{") 
        ("citetitles" "[{") 
        ("headlessfullcite" "[{")))

(setq reftex-cite-prompt-optional-args nil)
(setq reftex-cite-cleanup-optional-args t)


(setq org-latex-pdf-process
  '("latexmk -pdflatex='pdflatex -interaction nonstopmode' -pdf -bibtex -f %f"))

Magit – gotta love git

(use-package magit
  :init (setq magit-diff-options '("-b")) ; ignore whitespace
  :bind ("C-x g" . magit-status)
 )

Misc packages

Who doesn’t love markdown mode. I know I do. Go for GitHub markdown as necessary.

(use-package writeroom-mode
 )

Let’s make emacs a little prettier

(require 'solarized-theme)
(load-theme 'solarized-dark)
(setq x-underline-at-descent-line t)

Let’s get something interesting to read, using RSS feeds

;; use an org file to organise feeds
(use-package elfeed-org
  :ensure t
  :config
  (elfeed-org)
  (setq rmh-elfeed-org-files (list "~/NAS/Org/rssFeeds.org")))

(global-set-key (kbd "C-x w") 'elfeed)

Ox-reveal

This is based on Scott Nesbitt’s post How to create slides with Emacs Org mode and Reveal.js where he says:

You’ll also need to install Org-Reveal, and grab a copy of the Reveal.js archive. You can unpack Reveal.js anywhere you like. I put it in the folder ~/slides/reveal.js, where I store my slides.

Once you’ve done all that, add the following to your .emacs file to integrate Org-Reveal into Emacs:

#+begine_src emacs-lisp :tangle yes ;; Reveal.js + Org mode (require ‘ox-reveal) (setq Org-Reveal-root “file://reveal.js”) (setq Org-Reveal-title-slide nil)

In the above, path-to-reveal.js is where you unpacked the Reveal.js archive. In my setup, that entry is file:///home/scott/slides/reveal.js.

That was a bit of work. Luckily, you only need to do it once. Now, you’re ready to go.

Creating your slide deck and slides

Fire up Emacs and create a new file. Name the file whatever you want, but make sure it has the extension .org. The extension tells Emacs it’s an Org mode file. Whenever you open a file with that extension, Emacs applies the correct syntax highlighting and makes the Org menu available on the menu bar.

Add the following information to the top of the file:

#+OPTIONS: num:nil toc:nil
#+REVEAL_TRANS: None/Fade/Slide/Convex/Concave/Zoom
#+REVEAL_THEME: Black/White/League/Sky/Beige/Simple/Serif/Blood/Night/Moon/Solarized
#+Title: Title of Your Talk
#+Author: Your Name
#+Email: Your Email Address or Twitter Handle

That block is like metadata for your slide deck. Let’s look at the first three items in that block:

num:nil and toc:nil suppress the numbering of headings and the creation of a table of contents when you generate your slides #REVEAL_TRANS controls the transition effect when you move between slides. I usually go with None, but feel free to experiment #REVEAL_THEME controls the look of the slides. I usually stick with Black or White, but, again, feel free to experiment Add a heading after the metadata block by typing an asterisk, followed by a space, followed by some text. This will be the title slide. (Make the title a good one!)

If you want to add your name below the title, press Enter. Emacs adds a blank, indented space below the heading. Type your name, and any other information, in that space.

Add new slides to the deck by adding headings (text with an asterisk and space in front of it, remember?) to the file. Simple, isn’t it?