The Org Mode feature was a big reason in my recent re-kindling of my Emacs love affair.
Initialization of Org Mode by hooking it into YASnippets, and other settings.
(use-package org
:ensure t ; But it comes with Emacs now!?
:init
(setq org-use-speed-commands t
org-return-follows-link t
org-hide-emphasis-markers t
org-completion-use-ido t
org-outline-path-complete-in-steps nil
org-src-fontify-natively t ;; Pretty code blocks
org-src-tab-acts-natively t
org-confirm-babel-evaluate nil
org-todo-keywords '((sequence "TODO(t)" "DOING(g)" "|" "DONE(d)")
(sequence "|" "CANCELED(c)")))
(add-to-list 'auto-mode-alist '("\\.txt\\'" . org-mode))
(add-to-list 'auto-mode-alist '(".*/[0-9]*$" . org-mode)) ;; Journal entries
(add-hook 'org-mode-hook 'yas-minor-mode-on)
:bind (("C-c l" . org-store-link)
("C-c c" . org-capture)
("C-M-|" . indent-rigidly))
:config
(font-lock-add-keywords ; A bit silly but my headers are now
'org-mode `(("^\\*+ \\(TODO\\) " ; shorter, and that is nice canceled
(1 (progn (compose-region (match-beginning 1) (match-end 1) "⚑")
nil)))
("^\\*+ \\(DOING\\) "
(1 (progn (compose-region (match-beginning 1) (match-end 1) "⚐")
nil)))
("^\\*+ \\(CANCELED\\) "
(1 (progn (compose-region (match-beginning 1) (match-end 1) "✘")
nil)))
("^\\*+ \\(DONE\\) "
(1 (progn (compose-region (match-beginning 1) (match-end 1) "✔")
nil)))))
(define-key org-mode-map (kbd "M-C-n") 'org-end-of-item-list)
(define-key org-mode-map (kbd "M-C-p") 'org-beginning-of-item-list)
(define-key org-mode-map (kbd "M-C-u") 'outline-up-heading)
(define-key org-mode-map (kbd "M-C-w") 'org-table-copy-region)
(define-key org-mode-map (kbd "M-C-y") 'org-table-paste-rectangle)
(define-key org-mode-map [remap org-return] (lambda () (interactive)
(if (org-in-src-block-p)
(org-return)
(org-return-indent)))))
Speed Commands: If point is at the beginning of a headline or
code block in org-mode, single keys do fun things. See
org-speed-command-help
for details (or hit the ? key at a
headline).
Note: For the most part, I like electric-indent-mode, however, it
doesn’t really play well with org-mode
, so I just bind the Return
key to the org-return-indent
function and get the same effect (but
only if I am not in a source code block…which actually insert
multiple new lines). This return and indent feature is fine, since
when I save a file, I automatically strip off trailing whitespace.
We will use some of the packages from org extras, especially org-drill and org-mime for HTML exports:
(use-package org-drill
:ensure org-plus-contrib)
(use-package org-mime
:ensure t)
Trying an experiment to see if I like inserting two spaces at the end of a sentence:
(defun ha/insert-two-spaces (N)
"Inserts two spaces at the end of sentences."
(interactive "p")
(when (looking-back "[!?.] " 2)
(insert " ")))
(advice-add 'org-self-insert-command :after #'ha/insert-two-spaces)
A couple of short-cut keys to make it easier to edit text.
(defun org-text-bold () "Wraps the region with asterisks."
(interactive)
(surround-text "*"))
(defun org-text-italics () "Wraps the region with slashes."
(interactive)
(surround-text "/"))
(defun org-text-code () "Wraps the region with equal signs."
(interactive)
(surround-text "="))
Now we can associate some keystrokes to the org-mode:
(use-package org
:config
(bind-keys :map org-mode-map
("A-b" . (surround-text-with "+"))
("s-b" . (surround-text-with "*"))
("A-i" . (surround-text-with "/"))
("s-i" . (surround-text-with "/"))
("A-=" . (surround-text-with "="))
("s-=" . (surround-text-with "="))
("A-`" . (surround-text-with "~"))
("s-`" . (surround-text-with "~"))
("C-s-f" . forward-sentence)
("C-s-b" . backward-sentence)))
From this discussion, I got the code to replace M-RET
in lists with
just RET
, so that Org acts more like other word processors.
(defun ha/org-return (&optional ignore)
"Add new list item, heading or table row with RET.
A double return on an empty element deletes it.
Use a prefix arg to get regular RET. "
(interactive "P")
(if ignore
(org-return)
(cond
;; Open links like usual
((eq 'link (car (org-element-context)))
(org-return))
;; lists end with two blank lines, so we need to make sure we are also not
;; at the beginning of a line to avoid a loop where a new entry gets
;; created with only one blank line.
((and (org-in-item-p) (not (bolp)))
(if (org-element-property :contents-begin (org-element-context))
(org-insert-heading)
(beginning-of-line)
(setf (buffer-substring
(line-beginning-position) (line-end-position)) "")
(org-return)))
((org-at-heading-p)
(if (not (string= "" (org-element-property :title (org-element-context))))
(progn (org-end-of-meta-data)
(org-insert-heading))
(beginning-of-line)
(setf (buffer-substring
(line-beginning-position) (line-end-position)) "")))
((org-at-table-p)
(if (-any?
(lambda (x) (not (string= "" x)))
(nth
(- (org-table-current-dline) 1)
(org-table-to-lisp)))
(org-return)
;; empty row
(beginning-of-line)
(setf (buffer-substring
(line-beginning-position) (line-end-position)) "")
(org-return)))
(t
(org-return)))))
(define-key org-mode-map (kbd "RET") #'ha/org-return)
Displaying the headers using various bullets are nice for my presentations.
(use-package org-bullets
:ensure t
:init (add-hook 'org-mode-hook 'org-bullets-mode))
Here is my approach for quickly making the initial asterisks for listing items and whatnot, appear as Unicode bullets (without actually affecting the text file or the behavior).
(use-package org
:init
(font-lock-add-keywords 'org-mode
'(("^ +\\([-*]\\) "
(0 (prog1 () (compose-region (match-beginning 1) (match-end 1) "•")))))))
Before we load org-mode
proper, we need to set the following
syntax high-lighting parameters. These are used to help bring out
the source code during literate programming mode.
Assuming the pandoc
project has been installed, we can take HTML
code, copied from a browser into the system’s clipboard, and convert
it to org-mode
format before yanking it into buffer.
(defun ha/paste-html-to-org ()
"Assumes the contents of the system clip/paste-board to be
HTML, this calls out to `pandoc' to convert it for the org-mode
format."
(interactive)
(let* ((clip (if (eq system-type 'darwin)
"pbpaste -Prefer rts"
"xclip -out -selection 'clipboard' -t text/html"))
(format (if (eq mode-name "Org") "org" "markdown"))
(pandoc (concat "pandoc -f rts -t " format))
(cmd (concat clip " | " pandoc))
(text (shell-command-to-string cmd)))
(kill-new text)
(yank)))
Didn’t realize that org-journal essentially does what I have been doing by hand. With a little customization, I don’t have to change anything else:
(use-package org-journal
:ensure t
:init
(setq org-journal-dir "~/journal/")
(setq org-journal-date-format "#+TITLE: Journal Entry- %e %b %Y (%A)")
(setq org-journal-time-format ""))
The time format is the heading for each section. I set it to a blank since I really don’t care about the time I add a section.
A function to easily load today (and yesterday’s) journal entry.
(defun get-journal-file-today ()
"Return filename for today's journal entry."
(let ((daily-name (format-time-string "%Y%m%d")))
(expand-file-name (concat org-journal-dir daily-name))))
(defun journal-file-today ()
"Create and load a journal file based on today's date."
(interactive)
(find-file (get-journal-file-today)))
(global-set-key (kbd "C-c f j") 'journal-file-today)
Since I sometimes (not often) forget to create a journal entry, and need to re-write history.
(defun get-journal-file-yesterday ()
"Return filename for yesterday's journal entry."
(let* ((yesterday (time-subtract (current-time) (days-to-time 1)))
(daily-name (format-time-string "%Y%m%d" yesterday)))
(expand-file-name (concat org-journal-dir daily-name))))
(defun journal-file-yesterday ()
"Creates and load a file based on yesterday's date."
(interactive)
(find-file (get-journal-file-yesterday)))
(global-set-key (kbd "C-c f y") 'journal-file-yesterday)
Nice to automatically insert a specific header if the journal entry file is empty using auto-insert.
When I create a new journal entry, I want a snappy title and a checklist of daily tasks. The template should insert a date that matches the file’s name, not necessarily the current date.
Also the inserted daily information and check-lists should only happen if I am creating today’s journal, not catching up with the past… oh, and we might have special dailies to be inserted based on the day of the week. Guess I could use YAS snippets, but then the code amount of code would over-shadow the text, so we’ll make a function.
(defun journal-file-insert ()
"Insert's the journal heading based on the file's name."
(interactive)
(let* ((year (string-to-number (substring (buffer-name) 0 4)))
(month (string-to-number (substring (buffer-name) 4 6)))
(day (string-to-number (substring (buffer-name) 6 8)))
(datim (encode-time 0 0 0 day month year)))
(insert (format-time-string org-journal-date-format datim))
(insert "\n\n $0\n") ; Start with a blank separating line
;; Note: The `insert-file-contents' leaves the cursor at the
;; beginning, so the easiest approach is to insert these files
;; in reverse order:
;; If the journal entry I'm creating matches today's date:
(when (equal (file-name-base (buffer-file-name))
(format-time-string "%Y%m%d"))
(insert-file-contents "journal-dailies-end.org")
;; Insert dailies that only happen once a week:
(let ((weekday-template (downcase
(format-time-string "journal-%a.org"))))
(when (file-exists-p weekday-template)
(insert-file-contents weekday-template)))
(insert-file-contents "journal-dailies.org")
(insert "$0")
(let ((contents (buffer-string)))
(delete-region (point-min) (point-max))
(yas-expand-snippet contents (point-min) (point-max))))))
(define-auto-insert "/[0-9]\\{8\\}$" [journal-file-insert])
To use this, make the following files:
journal-dailies.org
to contain the real dailiesjournal-dailies-end.org
to contain any follow-up notesjournal-mon.org
for additional text to be inserted on Monday journals- And a
journal-XYZ.org
for each additional weekday
I really would really like to read what I did last year “at this time”, and by that, I mean, 365 days ago, plus or minus a few to get to the same day of the week.
(defun journal-last-year-file ()
"Returns the string corresponding to the journal entry that
happened 'last year' at this same time (meaning on the same day
of the week)."
(let* ((last-year-seconds (- (float-time) (* 365 24 60 60)))
(last-year (seconds-to-time last-year-seconds))
(last-year-dow (nth 6 (decode-time last-year)))
(this-year-dow (nth 6 (decode-time)))
(difference (if (> this-year-dow last-year-dow)
(- this-year-dow last-year-dow)
(- last-year-dow this-year-dow)))
(target-date-seconds (+ last-year-seconds (* difference 24 60 60)))
(target-date (seconds-to-time target-date-seconds)))
(format-time-string "%Y%m%d" target-date)))
(defun journal-last-year ()
"Loads last year's journal entry, which is not necessary the
same day of the month, but will be the same day of the week."
(interactive)
(let ((journal-file (concat org-journal-dir (journal-last-year-file))))
(find-file journal-file)))
(global-set-key (kbd "C-c f L") 'journal-last-year)
I’ve notice that while I really like taking notes in a meeting, I don’t always like the multiple windows I have opened, so I created this function that I can easily call to eliminate distractions during a meeting.
(defun meeting-notes ()
"Call this after creating an org-mode heading for where the notes for the meeting
should be. After calling this function, call 'meeting-done' to reset the environment."
(interactive)
(outline-mark-subtree) ;; Select org-mode section
(narrow-to-region (region-beginning) (region-end)) ;; Only show that region
(deactivate-mark)
(delete-other-windows) ;; Get rid of other windows
(text-scale-set 2) ;; Text is now readable by others
(fringe-mode 0)
(message "When finished taking your notes, run meeting-done."))
Of course, I need an ‘undo’ feature when the meeting is over…
(defun meeting-done ()
"Attempt to 'undo' the effects of taking meeting notes."
(interactive)
(widen) ;; Opposite of narrow-to-region
(text-scale-set 0) ;; Reset the font size increase
(fringe-mode 1)
(winner-undo)) ;; Put the windows back in place
I keep all my org-mode
files in a few directories, and I would
like them automatically searched when I generate agendas.
(setq org-agenda-files '("~/Dropbox/org/personal"
"~/Dropbox/org/technical"
"~/Dropbox/org/project"))
Let’s say you were in the middle of something, but would like to take a quick note, but without affecting the file you are working on. This is called a “capture”, and is bound to the following key:
General notes are stored in @SUMMARY.org, and tasks synced with my Google Task list are stored in tasks.org:
(defvar org-default-notes-file "~/personal/@SUMMARY.org")
(defvar org-default-tasks-file "~/personal/tasks.org")
This will bring up a list of note capturing templates. I actually override this in my system-specific “local” configuration file.
(defun ha/first-header ()
(goto-char (point-min))
(search-forward-regexp "^\* ")
(beginning-of-line 1)
(point))
(setq org-capture-templates
'(("n" "Thought or Note" entry
(file org-default-notes-file)
"* %?\n\n %i\n\n See: %a" :empty-lines 1)
("j" "Journal Note" entry
(file (get-journal-file-today))
"* %?\n\n %i\n\n From: %a" :empty-lines 1)
("t" "Task Entry" entry
(file+function org-default-tasks-file ha/load-org-tasks)
"* %?\n\n %i\n\n From: %a" :empty-lines 1)
("w" "Website Announcement" entry
(file+function "~/website/index.org" ha/first-header)
"* %?
:PROPERTIES:
:PUBDATE: %t
:END:
#+HTML: <div class=\"date\">%<%e %b %Y></div>
%i
[[%F][Read more...]" :empty-lines 1)))
After you have selected the template, you type in your note and hit
C-c C-c
to store it in the file listed above.
Just remember, at some point to hit C-c C-w
to refile that note
in the appropriate place.
Using org-michel for syncing a single Org file with my Google Tasks.
pip install google-api-python-client python-gflags python-dateutil httplib2
pip install urllib3 apiclient discovery
pip install --upgrade oauth2client
hg clone https://bitbucket.org/edgimar/michel-orgmode
The problem is the --sync
doesn’t work. So, whenever I read the
file, I pull it down first. On save, I push it:
(defun ha/load-org-tasks ()
(interactive)
(shell-command (format "/usr/local/bin/michel-orgmode --pull --orgfile %s" org-default-tasks-file))
(find-file org-default-tasks-file)
(ha/first-header)
(add-hook 'after-save-hook 'ha/save-org-tasks t t))
(defun ha/save-org-tasks ()
(save-buffer)
(shell-command (format "/usr/local/bin/michel-orgmode --push --orgfile %s" org-default-tasks-file)))
Seems some change now requires a direct load of HTML:
To make the org-mode
export defaults closer to my liking
(without having to put specific #+PROPERTY commands), I get rid of
the postamble, and then configure the default fonts.
(use-package ox-html
:init
(setq org-html-postamble nil)
(setq org-export-with-section-numbers nil)
(setq org-export-with-toc nil)
(setq org-html-head-extra "
<link href='http://fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic&subset=latin,latin-ext' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Source+Code+Pro:400,700' rel='stylesheet' type='text/css'>
<style type='text/css'>
body {
font-family: 'Source Sans Pro', sans-serif;
}
pre, code {
font-family: 'Source Code Pro', monospace;
}
</style>"))
I alternated between the browser-based presentation tool, reveal.js and staying in Emacs with org-tree-slide.
Generate presentations from my org-mode files using org-reveal. Just download and make the results available to the HTML output:
(use-package ox-reveal
:init
(setq org-reveal-root (concat "file://" (getenv "HOME") "/Public/js/reveal.js"))
(setq org-reveal-postamble "Howard Abrams"))
A quick way to display an org-mode file is using org-tree-slide.
- org-tree-slide-move-next-tree (C->)
- org-tree-slide-move-previous-tree (C-<)
- org-tree-slide-content (C-x s c)
(use-package org-tree-slide
:ensure t
:init
(setq org-tree-slide-skip-outline-level 4)
(org-tree-slide-simple-profile))
The trick to literate programming is in the Babel project, which allows org-mode to not only interpret source code blocks, but evaluate them and tangle them out to a file.
(use-package org
:config
(add-to-list 'org-src-lang-modes '("dot" . "graphviz-dot"))
(org-babel-do-load-languages 'org-babel-load-languages
'((shell . t)
(js . t)
(emacs-lisp . t)
(perl . t)
(scala . t)
(clojure . t)
(python . t)
(ruby . t)
(dot . t)
(css . t)
(plantuml . t))))
This setting also addresses the issue to associate the dot
language
with the graphviz-dot
mode.
It seems to automatically recognize the language used in a source
block, but if not, call org-babel-lob-ingest
to add all the
languages from the code blocks in a particular file into the list
that Babel supports. Keystroke: C-c C-v i
.
According to the narrow-widen article, we can have C-x C-s
get
out of editing org-mode source code blocks:
(eval-after-load 'org-src
'(define-key org-src-mode-map
(kbd "C-x C-s") #'org-edit-src-exit))
I’m normally fine with having my code automatically evaluated.
(setq org-confirm-babel-evaluate nil)
Once upon a time, fontifying individual code blocks made it
impossible to edit the block without org-edit-special
. Now that
the syntax rendering is faster, I keep it on.
(setq org-src-fontify-natively t)
(setq org-src-tab-acts-natively t)
Need to provide the init-org-mode
so that I can require this
package.
(provide 'init-org-mode)
Before you can build this on a new system, make sure that you put
the cursor over any of these properties, and hit: C-c C-c