Integrating Emacs’ org-mode with the Awesome window manager
I’ve switched to another tiling window manager called awesome and it’s really like its name suggests. It’s more a window manager framework that can be programmed in lua to create a window manager that does exactly what you want it to do.
Awesome has a lua library called naughty for displaying notifications. Now my idea was that whenever my mouse pointer enters the region of the textbox widget that shows the current date and time in a wibox, a popup should show the agenda for this week and dispose automatically when I move the mouse away.
Ok, so here’s the code on the emacs side. It assures that the file /tmp/org-agenda.txt always contains a plain-text export of the current org agenda. It’s created once on emacs startup and after each change to any org agenda file it’ll be updated.
;; update agenda file after changes to org files
(defun th-org-mode-init ()
(add-hook 'after-save-hook 'th-org-update-agenda-file t t))
(add-hook 'org-mode-hook 'th-org-mode-init)
;; that's the export function
(defun th-org-update-agenda-file (&optional force)
(interactive)
(save-excursion
(save-window-excursion
(let ((file "/tmp/org-agenda.txt"))
(org-agenda-list)
(org-write-agenda file)))))
;; do it once at startup
(th-org-update-agenda-file t)
And here’s the lua code on the awesome side:
-- the current agenda popup
org_agenda_pupup = nil
-- do some highlighting and show the popup
function show_org_agenda ()
local fd = io.open("/tmp/org-agenda.txt", "r")
if not fd then
return
end
local text = fd:read("*a")
fd:close()
-- highlight week agenda line
text = text:gsub("(Week%-agenda[ ]+%(W%d%d?%):)", "%1")
-- highlight dates
text = text:gsub("(%w+[ ]+%d%d? %w+ %d%d%d%d[^n]*)", "%1")
-- highlight times
text = text:gsub("(%d%d?:%d%d)", "%1")
-- highlight tags
text = text:gsub("(:[^ ]+:)([ ]*n)", "%1%2")
-- highlight TODOs
text = text:gsub("(TODO) ", "%1 ")
-- highlight categories
text = text:gsub("([ ]+%w+:) ", "%1 ")
org_agenda_pupup = naughty.notify(
{ text = text,
timeout = 999999999,
width = 600,
position = "bottom_right",
screen = mouse.screen })
end
-- dispose the popup
function dispose_org_agenda ()
if org_agenda_pupup ~= nil then
naughty.destroy(org_agenda_pupup)
org_agenda_pupup = nil
end
end
mydatebox = widget({ type = "textbox", align = "right" }) -- shows the date
mydatebox.mouse_enter = show_org_agenda
mydatebox.mouse_leave = dispose_org_agenda
-- after that the mydatebox is added to some wibox, of course...
And that’s how it looks like.
Calling org-remember from inside conkeror
I use Carsten Dominik’s great emacs package org-mode for project planning, appointments and TODOs (org-mode homepage). One cool feature is that it integrates nicely with the remember package. So when an idea comes into my mind I need to remember for later, I hit M-x org-remember RET which presents me a buffer with a custom template, I type my note and hit C-c C-c. The note is filed then and the buffer is gone, so there’s really no interruption.
One cool thing is that org-remember automatically inserts a link to the “thing” you had open when adding the note. This “thing” could be a info buffer, a mail, a usenet article, an image,…
I thought it would be nice to use that facility to make TODO-bookmarks like “TODO read that great blog entry!” from inside my browser conkeror. And so I did.
Here’s the emacs side of the code (put it in your ~/.emacs):
(defun th-org-remember-conkeror (url)
(interactive "s")
(org-remember nil ?t)
(save-excursion
(insert "\n\n [[" url "]]"))
(local-set-key (kbd "C-c C-c")
(lambda ()
(interactive)
(org-ctrl-c-ctrl-c)
(delete-frame nil t))))
And that’s the conkeror side (put it in your ~/.conkerorrc):
function org_remember(url, window) {
var cmd_str = 'emacsclient -c --eval \'(th-org-remember-conkeror "' + url + '")\'';
if (window != null) {
window.minibuffer.message('Issuing ' + cmd_str);
}
shell_command_blind(cmd_str);
}
interactive("org-remember", "Remember the current url with org-remember",
function (I) {
org_remember(I.buffer.display_URI_string, I.window);
});
This code may require emacs 23, I’m not too sure. What you need in every case is a running emacs server, so that you can connect to it with emacsclient.
Re-open read-only files as root automagically
I have my emacs running as server and two small wrapper scripts ec and et which connect to it with emacsclient (either creating a new frame or opening a terminal frame).
Because I want to do all my system administration (editing files in /etc/) with emacs and don’t want to start another emacs instance as root, I use TRAMP to switch to superuser mode automagically if the opened file is read-only.
Here’s the code (UPDATED: Uses defadvice instead of hooking into find-file-hook, which had the bad effect of find-file-hook running twice.):
(defun th-rename-tramp-buffer ()
(when (file-remote-p (buffer-file-name))
(rename-buffer
(format "%s:%s"
(file-remote-p (buffer-file-name) 'method)
(buffer-name)))))
(add-hook 'find-file-hook
'th-rename-tramp-buffer)
(defadvice find-file (around th-find-file activate)
"Open FILENAME using tramp's sudo method if it's read-only."
(if (and (not (file-writable-p (ad-get-arg 0)))
(y-or-n-p (concat "File "
(ad-get-arg 0)
" is read-only. Open it as root? ")))
(th-find-file-sudo (ad-get-arg 0))
ad-do-it))
(defun th-find-file-sudo (file)
"Opens FILE with root privileges."
(interactive "F")
(set-buffer (find-file (concat "/sudo::" file))))
Now whenever I type ec /some/file/i/have/no/permissions/to/write (or emacsclient [-c|-t] /some/root/file, emacs asks me to re-open it using TRAMP’s sudo method.
Some calendar and org-mode integration stuff
The following code binds RET in calendar mode to a function that opens an org agenda buffer for that day (or better that week).
(defun th-calendar-open-agenda ()
(interactive)
(let* ((calendar-date (or
;; the date at point in the calendar buffer
(calendar-cursor-to-date)
;; if there's none, use the curren date
(calendar-current-date)))
(day (time-to-days (encode-time 1 1 1
(second calendar-date)
(first calendar-date)
(third calendar-date))))
(calendar-buffer (current-buffer)))
(org-agenda-list nil day)
(select-window (get-buffer-window calendar-buffer))))
(define-key calendar-mode-map (kbd "RET") 'th-calendar-open-agenda)
And here’s a small minor mode which uses the function above to refresh the agenda buffer when you move point in the calendar buffer, so calendar and agenda stay in sync.
(define-minor-mode th-org-agenda-follow-calendar-mode
"If enabled, each calendar movement will refresh the org agenda
buffer."
:lighter " OrgAgendaFollow"
(if (not (eq major-mode 'calendar-mode))
(message "Cannot activate th-org-agenda-follow-calendar-mode in %s." major-mode)
(if th-org-agenda-follow-calendar-mode
(add-hook 'calendar-move-hook 'th-calendar-open-agenda)
(remove-hook 'calendar-move-hook 'th-calendar-open-agenda))))
(add-hook 'calendar-mode-hook 'th-org-agenda-follow-calendar-mode)
Another thing I added to calendar is the display of the week-of-year in the mode-line.
(add-to-list 'calendar-mode-line-format
'(let ((day (nth 1 date))
(month (nth 0 date))
(year (nth 2 date)))
(format-time-string "Week of year: %V"
(encode-time 1 1 1 day month year))))
My funky ZSH prompt
Here’s the config for my ZSH prompt:
local blue_op="%{$fg[blue]%}[%{$reset_color%}"
local blue_cp="%{$fg[blue]%}]%{$reset_color%}"
local path_p="${blue_op}%~${blue_cp}"
local user_host="${blue_op}%n@%m${blue_cp}"
local ret_status="${blue_op}%?${blue_cp}"
local hist_no="${blue_op}%h${blue_cp}"
local smiley="%(?,%{$fg[green]%}:%)%{$reset_color%},%{$fg[red]%}:(%{$reset_color%})"
PROMPT="╭─${path_p}─${user_host}─${ret_status}─${hist_no}
╰─${blue_op}${smiley}${blue_cp} %# "
local cur_cmd="${blue_op}%_${blue_cp}"
PROMPT2="${cur_cmd}> "
It looks like this. 
It has two lines. The first displays the path of the current working directory, then the user and hostname, then the return value of the last executed command, and the last item is the command’s number in the history.
The second line displays a green smiley, if the last command worked well, or a red smiley, if it failed.
I took some inspirations from the prompt of strcat, see http://www.echox.de/blog/archives/74-ZSH-Prompt.html.
