Category Archives: emacs-lisp

Org Mode: Create a quick timestamped note and capture a screenshot

I wanted to be able to quickly create timestamped notes and possibly capture a screenshot. Prompting for a value inside an org-capture-template disrupts my screen a little, so maybe this will make it as easy as possible. I could probably do this without going through org-capture-templates, but I wanted to take advantage of the fact that Org Mode will deal with the date tree and finding the right position itself.

(use-package org
  (add-to-list 'org-capture-templates
               '("p" "Podcast log - timestamped" item
                 (file+olp+datetree "~/orgzly/")
                 "%<%H:%M:%S,%3N> %^{Note}"
                 :immediate-finish t)))
  (defun my/org-capture-prefill-template (template &rest values)
    "Pre-fill TEMPLATE with VALUES."
    (setq template (or template (org-capture-get :template)))
      (insert template)
      (goto-char (point-min))
      (while (re-search-forward
              (concat "%\\("
                      "\\)") nil t)
        (if (car values)
            (replace-match (car values) nil t))
        (setq values (cdr values)))
(defun my/capture-screenshot (time &optional note)
  "Capture screenshot and save it to a file labeled with TIME and NOTE.
Return the filename."
  (interactive (list (current-time) (read-string "Note: ")))
  (let* ((filename (expand-file-name
                        (concat "Screenshot_"
                                (format-time-string "%Y%0m%d_%H%M%S" time)
                                (if note (concat " " note) "")
         (cmd (concat "spectacle -b -o "
                      (shell-quote-argument filename))))
    (shell-command cmd)
(defun my/capture-timestamped-note (time note)
  "Disable Helm and capture a quick timestamped note."
  (interactive (list (current-time) (read-string "Note: ")))
  (let ((helm-completing-read-handlers-alist '((org-capture . nil)))
        (entry (org-capture-select-template "p")))
    (org-capture-set-plist entry)
     :template (org-capture-fill-template
                (my/org-capture-prefill-template (org-capture-get :template)
                                                 (format-time-string "%H:%M:%S,%3N")
(defun my/capture-timestamped-note-with-screenshot (time note)
  (interactive (list (current-time) (read-string "Note: ")))
  (kill-new (my/capture-screenshot time note))
  (my/capture-timestamped-note time note))

Then I can call it with h h n for my/capture-timestamped-note or h h i for my/capture-timestamped-note-with-screenshot via keyboard shortcuts defined elsewhere in my config (see my/key-chord-commands).

Emacs: Strike through headlines for DONE tasks in Org

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.


Here’s the code:

(setq org-fontify-done-headline t)
 '(org-done ((t (:foreground "PaleGreen"   
                 :weight normal
                 :strike-through t))))
            ((((class color) (min-colors 16) (background dark)) 
               (:foreground "LightSalmon" :strike-through t)))))

View my Emacs configuration

Emacs: Working with multiple source trees

As a developer, I often find myself working with multiple trees of source code. Sometimes, I’m comparing the trunk and the branches. Sometimes, I’m copying ideas from one place to another. Sometimes, I’m working on one project and an urgent defect comes in for a different project. With good development environments such as Eclipse, I’d still need to click on the project or directory in order to change my context, and then use a keyboard shortcut to find the resource in that tree.

With an awesome development environment like my customized Emacs, however, I can easily juggle multiple source trees. Here’s the macro I wrote to make setting this up much easier:

(defmacro sacha/file-cache-setup-tree (prefix shortcut directories)
  "Set up the file-cache tree for PREFIX using the keyboard SHORTCUT.
DIRECTORIES should be a list of directory names."
  `(let ((file-cache-alist nil)
	 (directories ,directories))
     (while directories
       (file-cache-add-directory-using-find (car directories))
       (setq directories (cdr directories)))
     (setq ,(intern (concat "sacha/file-cache-" prefix "-alist")) file-cache-alist)
     (defun ,(intern (concat "sacha/file-cache-ido-find-" prefix)) ()
       (let ((file-cache-alist ,(intern (concat "sacha/file-cache-" prefix "-alist"))))
	 (call-interactively 'file-cache-ido-find-file)))
     (global-set-key (kbd ,shortcut)
		     (quote ,(intern (concat "sacha/file-cache-ido-find-" prefix))))))

With that, I can use the following to map C-c p to finding files in my personal directories:

 "C-c p"

I’ve also set up keyboard shortcuts for the other trees I’m working on.

You’ll need file-cache, ido, and probably something like this:

(require 'filecache)
(require 'ido)
(defun file-cache-ido-find-file (file)
  "Using ido, interactively open file from file cache'.
First select a file, matched using ido-switch-buffer against the contents
in `file-cache-alist'. If the file exist in more than one
directory, select directory. Lastly the file is opened."
  (interactive (list (file-cache-ido-read "File: "
                                           (lambda (x)
                                             (car x))
  (let* ((record (assoc file file-cache-alist)))
      (if (= (length record) 2)
          (car (cdr record))
         (format "Find %s in dir: " file) (cdr record)))))))

(defun file-cache-ido-read (prompt choices)
  (let ((ido-make-buffer-list-hook
	 (lambda ()
	   (setq ido-temp-list choices))))
    (ido-read-buffer prompt)))
(add-to-list 'file-cache-filter-regexps "docs/html")
(add-to-list 'file-cache-filter-regexps "\\.svn-base$")
(add-to-list 'file-cache-filter-regexps "\\.dump$")

Emacs and w3m: Fake your user agent

In an ideal world, you would never need to make your browser pretend to be a different browser. In reality, a number of websites check for specific browsers such as Mozilla or Internet Explorer, or even specific versions of those browsers. Other websites check for popular search engine crawlers such as the Googlebot in order to display content optimized for that search engine. You may want to change your user agent to work around such limitations, or you might want to change your user agent string just for fun.

The following code allows you to set your user agent (wicked/w3m-set-user-agent), reload the current page using a specified user agent (wicked/w3m-reload-this-page-with-user-agent), and define regular expression matches for URLs to control user agent strings (wicked/w3m-fake-user-agent-sites). To use this, add the following to your ~/.emacs:

 (defvar wicked/w3m-fake-user-agents ;; (1)
   `(("w3m" . ,(concat "Emacs-w3m/" emacs-w3m-version " " w3m-version))
     ("ie6" . "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)")
     ("ff3" . "Mozilla/5.0 (X11; U; Linux i686; en-US; rv: Gecko/2008070206 Firefox/3.0.1")
     ("ff2" . "Mozilla/5.0 (X11; U; Linux i686; en-US; rv: Gecko/20080208 Firefox/")
     ("ie7" . "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727)")
     ("ie5.5" . "Mozilla/4.0 (compatible; MSIE 5.5; Windows 98)")
     ("iphone" . "Mozilla/5.0 (iPhone; U; CPU iPhone OS 2_0 like Mac OS X; en-us) AppleWebKit/525.18.1 (KHTML, like Gecko) Version/3.1.1 Mobile/5A347 Safari/525.20")
     ("safari" . "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_2; en-us) AppleWebKit/525.13 (KHTML, like Gecko) Version/3.1 Safari/525.13")
     ("google" . "Mozilla/5.0 (compatible; Googlebot/2.1; +"))
   "*Associative list of user agent names and strings.")

 (defvar wicked/w3m-fake-user-agent-sites ;; (2)
   '(("^https?://www\\.useragentstring\\.com" . "ff2"))
   "*Associative list of regular expressions matching URLs and the agent keyword or value.
 The first matching entry will be used.")

 (defun wicked/w3m-set-user-agent (agent)
   "Set the user agent to AGENT based on `wicked/w3m-fake-user-agents'.
 If AGENT is not defined in `wicked/w3m-fake-user-agents', it is used as the user agent.
 If AGENT is empty, the default w3m user agent will be used."
     (completing-read "User-agent [w3m]: "
                    (mapcar 'car wicked/w3m-fake-user-agents)
                    nil nil nil nil "w3m"))) ;; (3)
   (if agent
        (setq w3m-user-agent
               (and (string= agent "") (assoc "w3m" wicked/w3m-fake-user-agents)) ;; (4)
               (cdr (assoc agent wicked/w3m-fake-user-agents)) ;; (5)
               agent)) ;; (6)
        (setq w3m-add-user-agent t))
     (setq w3m-add-user-agent nil)))

 (defun wicked/w3m-reload-this-page-with-user-agent (agent)
   "Browse this page using AGENT based on `wicked/w3m-fake-user-agents'.
 If AGENT is not defined in `wicked/w3m-fake-user-agents', it is used as the user agent.
 If AGENT is empty, the default w3m user agent will be used."
   (interactive (list (completing-read "User-agent [w3m]: "
                    (mapcar 'car wicked/w3m-fake-user-agents)
                    nil nil nil nil "w3m")))
   (let ((w3m-user-agent w3m-user-agent)
       (w3m-add-user-agent w3m-add-user-agent))
     (wicked/w3m-set-user-agent agent) ;; (7)

 (defadvice w3m-header-arguments (around wicked activate) ;; (8)
   "Check `wicked/w3m-fake-user-agent-sites' for fake user agent definitions."
   (let ((w3m-user-agent w3m-user-agent)
         (w3m-add-user-agent w3m-add-user-agent)
         (sites wicked/w3m-fake-user-agent-sites))
     (while sites
       (if (string-match (caar sites) (ad-get-arg 1))
           (wicked/w3m-set-user-agent (cdar sites))
           (setq sites nil))
       (setq sites (cdr sites))))

wicked/w3m-fake-user-agents sets up a number of common user agents(1) using examples from If you frequently use other user agents, add them to this associative list. wicked/w3m-fake-user-agent-sites sets up some rules for URLs so that you can work around specific websites(2). The first matching rule will be used.

wicked/w3m-set-user-agent can be called from a w3m browser session to set the user agent for all new pages visited. By default, it uses the w3m user agent(3). It will also use the w3m user agent if the agent is blank(4). If the user agent is one of the frequently-used agents defined in wicked/w3m-fake-user-agents, then the corresponding user agent string will be used(5). If not, the string will be used as-is(6). If the agent is nil, the user agent string will be disabled.(7)

You can check a single page using a different user agent by using M-x wicked/w3m-reload-this-page-with-user-agent. It temporarily sets the user agent and then reloads the current page.(7)

The last segment of code modifies the behavior of w3m-header-arguments(8), matching wicked/w3m-fake-user-agents against the URL. This temporarily sets the user agent for matching sites.