Category Archives: emacs

Learn how to take notes more efficiently in Org Mode

How do you take notes in Org? Are you buried in a heap of uncategorized notes? Do you manually open the right file and navigate to the right heading? Are you mystified by org-capture and org-refile? Here’s a path that can help you learn how to more efficiently take (and organize!) notes in Org Mode.

  1. Set up a keyboard shortcut to go to your main Org file
  2. Use org-refile to file or jump to headings
  3. Use org-capture to write notes quickly
  4. Define your own org-capture templates for greater convenience
  5. Pull in additional information

Step 1. Set up a keyboard shortcut to go to your main Org file

Instead of using C-x C-f (find-file) all the time, set up shortcuts to jump to the Org files you use the most. This way, you can easily type that keyboard shortcut, go to the end of the file, and add your note. Here’s some sample code that sets the C-c o shortcut to open organizer.org in your home directory. You can add it to your ~/.emacs.d/init.el and then call M-x eval-buffer to load the changes.

(global-set-key (kbd "C-c o") 
                (lambda () (interactive) (find-file "~/organizer.org")))

Alternatively, you can use registers, which are Emacs data structures that can hold text, file references, and more. The following code sets the o register to organizer.org in your home directory:

(set-register ?o (cons 'file "~/organizer.org"))

You can then jump to it with C-x r j (jump-to-register), specifying o at the prompt.

Once you’re in your Org file, you can use M-> (end-of-buffer) to go to the end of the file, or you can use C-s (isearch-forward) to search for some text.

You’ll still need to switch back to your original buffer or window configuration when you’re done, but that’s something you can fix when you learn how to use org-capture.

Step 2. Use org-refile to file or jump to headings

The next improvement is to use org-refile to move the current subtree to a specified heading, or jump to one without moving any text. This will let you quickly go to a project or task from anywhere in Org Mode.

By default, org-refile will show you only the top-level headings of the current file. Let’s configure it to show you headings from all your agenda files. You can use M-x customize-variable to change org-refile-targets. Click on the INS button, then click on Value menu next to Identify target headline by. Change this to Max level number. In the Integer field, fill in a suitably high number, like 6. This is the maximum depth of headings that will be shown.

If you prefer to set your variables using Emacs Lisp, here’s the code that you can add it to your ~/.emacs.d/init.el. Call M-x eval-buffer to load the changes.

(setq org-refile-targets '((org-agenda-files . (:maxlevel . 6))))

Be sure to add your main Org Mode file to your agenda list. You can do so by going to the file and typing C-c [ (org-agenda-file-to-front), or by setting the org-agenda-files variable.

The standard Emacs completion interface isn’t as friendly as it could be. I use the Helm package to make it easier to select and complete input. Since Helm can be a little complex, you may want to start with ido-mode instead. Here’s how you can set Ido up to use with Org Mode:

(ido-mode)
(setq org-completion-use-ido t)

Once you’ve set up your org-refile-targets, your agenda files, and either Helm or Ido, you can get the hang of using org-refile. The standard keyboard shortcut for org-refile is C-c C-w when you’re in an Org Mode buffer. org-refile can do different things depending on how you call it:

  • By default, it moves the current subtree to the specified location.
  • If you call it with the prefix argument C-u (like so: C-u C-c C-w), it jumps to the specified location instead of moving the current subtree.
  • If you call it as C-u C-u C-c C-w, it jumps to the previous refiling location.

First, practise using it with the prefix argument (C-u C-c C-w) to jump to a location. Once you’ve gotten the hang of that, go to some of your uncategorized entries and use org-refile without the prefix argument (just C-c C-w) to move the entries to the right place.

org-refile gives you a quick way to jump to a heading, but you still have to find your way back to whatever you were working on before you wanted to take a note. After you’re comfortable with refiling notes to the right place, move on to learning how to use org-capture to quickly take notes from anywhere.

Step 3. Use org-capture to write notes quickly

org-capture can help you take notes quickly by popping up a window or leading you through prompts. When you’re done taking the note, it will return you to whatever you were looking at before you started. In order to take advantage of this, though, you’ll need to customize org-capture.

The Org Mode manual recommends giving org-capture a global keyboard shortcut such as C-c c.

(global-set-key (kbd "C-c c") 'org-capture)

You can use M-x customize-variable to set org-default-notes-file to the filename you would like notes to be saved to, or set it in Emacs Lisp code like this:

(setq org-default-notes-file "~/organizer.org")

Make sure that the file exists and is automatically opened in Org Mode.

If you type C-c c, org-capture will display a prompt. t is a simple task template, and C will show you the customization interface for org-capture-templates.

Let’s start with t. It will show you a buffer with a simple TODO entry. You can fill in the rest of the details, use C-c C-s (org-schedule) to schedule it for a particular day, set the deadline with C-c C-d (org-deadline), etc. You can change the TODO keyword or delete it.

When you’re done, type C-c C-c to automatically save it to your default notes file (as specified by org-default-notes-file). Changed your mind? Cancel with C-c C-k. After either C-c C-c or C-c C-k, you should be back to whatever it was that you were working on.

Practise using C-c c (org-capture) to quickly jot down several tasks or notes. Then go to your notes file and use C-c C-w (org-refile) to move the notes to the right place.

You can also refile the notes right from the capture buffer. Instead of typing C-c C-c to finish your note, use C-c C-w to refile it.

Get the hang of using org-capture to take notes, organizing them every so often (maybe at the end of your day, or once a week?) or refiling them as you go.

Step 4. Define your own org-capture-templates for greater convenience

If you find yourself capturing different kinds of notes often or you want to capture in another format (table entry? list item?), invest the time in customizing org-capture-templates. In the beginning, you might find the Customize interface you get from M-x customize-variable org-capture-templates to be easier to work with than setting the values in plain Emacs Lisp, since the Customize interface lists the options. Read the documentation and look at examples of how other people have configured their org-capture-templates for more ideas. I have quite a few templates defined in my config, and http://doc.norang.ca/org-mode.html has a number of templates too.

Step 5. Pull in additional information

org-capture and org-refile are great when you’re at your computer, but what if you’re away? Quite a few people use MobileOrg to take quick notes on the go. I haven’t gotten around to setting that up for my workflow properly; instead, I use Evernote to jot quick notes on my phone. As part of my weekly review process, I look at the notes in my Evernote inbox and copy them into Emacs as needed.

You can manually copy information from your preferred non-Emacs note-taking tools, or you can figure out an automatic way of doing so. For example, I have some code to copy Evernote notes titled “Journal” into an Org Mode file structured by year-month-day.

Tweak your workflow!

Here’s a quick sketch showing some of your workflow options when it comes to capturing and organizing information with Org Mode. Which combination do you prefer, and how could you make it even better?

2015-02-09 Capturing Org Mode notes more efficiently -- index card #emacs #org #capture #refile

2015-02-09 Capturing Org Mode notes more efficiently – index card #emacs #org #capture #refile

Emacs: Peer-to-peer coaching is easier when you use impatient-mode to share your buffer

It turns out that coaching someone on Emacs goes really quickly when you can sit side by side. I’ve been helping Sean Miller with Org Mode and Emacs Lisp, with the condition that he has to turn those notes into blog posts. Since he happens to live in the same neighbourhood, this is pretty convenient.

E-mailing snippets back and forth would’ve been tedious, and working on only one of the computers would’ve slowed us down. I remembered Christopher Wellons‘ demonstration of impatient-mode, which lets you display your buffer contents on a web page that automatically updates when you make changes. Here’s what I did:

  1. Install the impatient-mode package.
  2. Call M-x httpd-start.
  3. Configure the firewall to allow incoming connections.
  4. Put the selected buffer into impatient-mode.
  5. Share the link with my IP address (form: http://my.ip.ad.dress:8080/imp/)

Then Sean was able to easily copy text from my screen and try things out on his computer. For example, we dug into org-capture-templates to find out how to create datetree items that were also scheduled on the specified date in the agenda, and we converted his manually-dated headings to include active timestamps so that those would be on the agenda too. It would’ve been a hassle to dictate or re-type the regular expressions, and since we were changing the code rapidly based on our conversation, e-mail would’ve been a hassle. Using impatient-mode worked out well.

What did I like about this two-computer setup? I had access to all my configuration files so that I could easily pick up snippets, and I could demonstrate interesting features as I figured out the code or explained my approach. At the same time, he had access to all of his files and he could take notes in his own buffer. We didn’t have to fuss about with keybindings.

This kind of setup could probably work with a virtual coaching session, too, especially if both people have multiple monitors or if there was an easy way to switch between views.

This is what the webpage looked like:

2015-02-06 11_31_01-sean

Neat, huh?

Experimental Emacs Hangout 2015-02-18

In which we talked about Hydra (0:11), Helm (0:22), packages (0:25), Quelpa/quse-package (0:29), EWW (0:30), Org Mode (0:40), global-flycheck-mode (0:51), widget, TRAMP (1:15), conferences/hangouts (1:26), more Hydra (1:28), Emacs Lisp (1:32), command-history (1:35), Emacs (1:38), plans for getting better (1:44), latexmk (1:49), and other things. (Times are approximate.)

Other questions/comments:

  • Not a question, but I just learned about command-history. Which is a great way to whip up a function. I’m amazed that I just discovered it. – Jonathan Hill
  • I saw something in 24.4 change log about a notification system. Could that maybe solve your problem? – Mitchell Hunter
  • On Air viewers cannot see Sasha due to Sridhar presenting. – Marc Tamsky
  • Anyone using tabs? – Levi Strope

The next Emacs Hangout will be on March 18, 2015, at 8 PM Toronto time (12 midnight GMT) https://plus.google.com/u/0/events/cbj3rg26d8j9ifaiff5uq00ncr4

Want to get notified about upcoming hangouts? You can sign up for notifications at http://eepurl.com/bbi-Ir . We’ll experiment with starting off with a mini-workshop/demo of Org tasks and agenda (or maybe Helm, if that’s more interesting).

Text chat:

JJ Asghar 8:01 PM Russell: http://blog.binchen.org/posts/notes-on-using-gnus.html
Sridhar Ratnakumar 8:01 PM http://www.srid.ca/emacs/
Michael Hoffman 8:03 PM hi sorry i need to hook up my camera
Jason Lewis 8:04 PM https://marmalade-repo.org/packages/winner-mode-enable
Michael Hoffman 8:05 PM brb tech issues
Michael Hoffman 8:07 PM sorry guys looks like my mic isn’t going to work without a reboot sure, thanks so much
Michael Hoffman 8:08 PM can someone repost that gmail/gnus link? that sounded awesome
Sridhar Ratnakumar 8:08 PM http://blog.binchen.org/posts/notes-on-using-gnus.html
Michael Hoffman 8:09 PM thanks Sridhar
Bob Erb 8:09 PM ace-jump-mode
Michael Hoffman 8:12 PM I find `guide-key-mode` very helpful for remembering keybindings as well
Sridhar Ratnakumar 8:13 PM https://github.com/abo-abo/hydra
Michael Hoffman 8:14 PM Sacha can you increase your font siz e? thanks
Michael Hoffman 8:19 PM that’s just an example this is my guide-key setup (require-package guide-key guide-key) (setq guide-key/guide-key-sequence ‘(“C-x” “C-c”)) (setq guide-key/recursive-key-sequence-flag t) (setq guide-key/idle-delay 0.7)
Sridhar Ratnakumar 8:20 PM pretty useful
Michael Hoffman 8:20 PM I don’t see any reason why not to set it up for everything C-x if you set it up with an idle-delay of 0.7 or something then it usually won’t bother you unless you can’t remember
JJ Asghar 8:23 PM ;; i need tab complete (define-key helm-map (kbd “<tab>”) ‘helm-execute-persistent-action)
Sridhar Ratnakumar 8:23 PM thx
JJ Asghar 8:23 PM
Sridhar Ratnakumar 8:23 PM (we are talking about https://github.com/emacs-helm )
Michael Hoffman 8:25 PM has anyone tried the eww web browser it’s definitely a curiosity more than a useful thing
Russell Black 8:25 PM I agree
Michael Hoffman 8:25 PM although i can see cases where it would be useful i keep my .emacs in version control
me 8:26 PM I’ve tried eww a little bit, but I haven’t quite gotten the hang of using it.
Michael Hoffman 8:26 PM i set up a macro to require or install a package
Jacob MacDonald 8:26 PM https://github.com/jaccarmac/quse-package
me 8:26 PM Mostly I just use browse-url to open things in Chrome
Michael Hoffman 8:27 PM ;; XXX: seems to hang when trying to (package-install) at compile time? (defmacro require-package (feature &optional package) “Require FEATURE or `package-install` PACKAGE. Default PACKAGE is FEATURE.” ;; for debugging (message “(require-package %s %s)” feature package) (let ((package (or package feature))) ; XXX: can I have a macro produce multiple forms instead of nesting like this? `(eval-and-compile (eval-when-compile (unless (or (require ‘,feature nil t) (fboundp ‘flycheck-byte-compile-dest-file)) ; don’t run within flycheck (package-install ‘,package))) (require ‘,feature nil t)))) WARNING: does not always work i gotta run guys, this has been great thanks so much sacha
Jacob MacDonald 8:28 PM See ya!
me 8:28 PM Bye, Michael! Oops, missed him
Jacob MacDonald 8:29 PM https://github.com/jaccarmac/dot-emacs-dot-d
me 8:30 PM Hi Howard!
Jacob MacDonald 8:31 PM I used Conkerer for a few months to get the Emacs experience with a decent frontend for JS, etc.
Sridhar Ratnakumar 8:32 PM eww renders images fine; but the layout is not correct
Russell Black 8:32 PM I have to run to an appointment. Thanks guys!
Howard Abrams 8:33 PM Never renders perfectly,. but if you are selective in what you are browsing, it is useful for things like programming documentation that fails to supply a info version.
[email protected] 8:33 PM jittery audio. reconnecting..
[email protected] 8:34 PM i can’t hear anyone
Sod Oscarfono 8:39 PM how about time tracking sacha?
Sridhar Ratnakumar 8:39 PM how many people here already use org-mode?
Jacob MacDonald 8:39 PM I use it primarily for literate programming and a little for note taking.
Sod Oscarfono 8:39 PM i love org-mode! couldn’t live without it
Bob Erb 8:40 PM couldn’t live w/o org-capture – allows mind like water
Sod Oscarfono 8:41 PM sorry no mic or camera here, but that is exactly what i needed thanks Sridhar!
me 8:45 PM Yay!
Sridhar Ratnakumar 8:45 PM Pomodoro org-pomodor
Sod Oscarfono 8:47 PM great stuff thanks Sridhar!
Jacob MacDonald 8:52 PM I’m interested in getting my config to work across multiple OSs. The “correct ” thing to do is to (defvar variable) i believe
Sridhar Ratnakumar 8:52 PM I’m yet to use emacs for git
Jacob MacDonald 8:52 PM It’s like any other linter, you have to prioritize the messages.
Sridhar Ratnakumar 8:53 PM anyone using Emacs 25? apparently supports concurrency
Jacob MacDonald 8:53 PM I build Emacs from Git, so that’s … fun Look at the use-package source .
Bob Erb 8:57 PM (when (memq window-system ‘(mac ns)) (exec-path-from-shell-initialize))
Sod Oscarfono 8:57 PM i just stick to nix based systems so that saves me any hassle there
Sod Oscarfono 9:04 PM you seasoned professionals maybe already know this but i recently discovered the ability to export org to open office docs and pdfs from org: C-c C-e o for open office C-c C-e p for pdf.
Jason Lewis 9:05 PM @sridhar, magit is really nice for using git from emacs
Sridhar Ratnakumar 9:06 PM http://www.srid.ca/emacs/ is generated from C-c C-e h h
Sod Oscarfono 9:10 PM hahaha… there’s an app for that -itis!
Bob Erb 9:11 PM there’s a mode for that
Jacob MacDonald 9:18 PM I’m out. Thanks for the great discussion! I hope to have more to contribute in the future.
Sod Oscarfono 9:18 PM do you key forward also in bash?
Sridhar Ratnakumar 9:18 PM Howard – what font do you use in Emacs, per the screenshot in http://www.howardism.org/Technical/Emacs/literate-devops.html ?
Jason Lewis 9:19 PM https://github.com/adobe-fonts/source-code-pro
Sod Oscarfono 9:25 PM workflow/project and time management/ literate programming. maybe a session where we scrutinize/optimise init files ? we spoke a bit about it already but its always good
Sod Oscarfono 9:30 PM i concur.. thanks sacha
Sod Oscarfono 9:32 PM cut and paste ninja over here.. hahaha
Jason Lewis 9:33 PM I’ve still not managed to make the jump to writing much lisp hope to one day
Bob Erb 9:34 PM URL for Sacha’s book?
me 9:34 PM http://sachachua.com/read-lisp-tweak-emacs
Jason Lewis 9:35 PM cool nice tip thanks
Zachary Kanfer 9:36 PM http://emacsnyc.org/videos.html#2014-06
Howard Abrams 9:40 PM http://www.howardism.org/Technical/Emacs/tao-of-emacs.html
Sod Oscarfono 9:40 PM for those who like sublime style layout i use the graphene starter kit which i think bundles “projectile” … i think.. bad memory sorry… anyways it is now my must have next to org-mode
Sod Oscarfono 9:41 PM i show those people org-mode and that usually gets them interseted
me 9:41 PM Sod: Nice!
Sod Oscarfono 9:42 PM for me the prospect of never having to change screens… ie buffers at will
Sod Oscarfono 9:43 PM yea and just being able to fire up a familiar environment on other systems is super pro
Bob Erb 9:46 PM Cat!
Sod Oscarfono 9:46 PM i’m currently experimenting with desktop publishing from emacs
Zachary Kanfer 9:48 PM my init file: https://bitbucket.org/zck/.emacs.d
Sod Oscarfono 9:48 PM org-mode to pdf to bypass creative cloud / scribus.. but learning latex also. ah thats what i needed to hear cheers jason for sure.. i’m glad i stopped by maybe we also look at where is emacs deficient? can we collaborate on a tool, a bug or whatever. good idea. or we spit out our command history for ideas
Jonathan Hill 9:53 PM I missed all the great suggestions.
Bob Erb 9:56 PM Thank you, Sacha.
Sod Oscarfono 9:56 PM thank you all so much
Jason Lewis 9:57 PM thanks Sacha! bye
Zachary Kanfer 9:57 PM bye! Thanks again, Sacha!

Windows: Pipe output to your clipboard, or how I’ve been using NodeJS and Org Mode together

It’s not easy being on Windows instead of one of the more scriptable operating systems out there, but I stay on it because I like the drawing programs. Cygwin and Vagrant fill enough gaps to keep me mostly sane. (Although maybe I should work up the courage to dual-boot Windows 8.1 and a Linux distribution, and then get my ScanSnap working.)

Anyway, I’m making do. Thanks to Node and the abundance of libraries available through NPM, Javascript is shaping up to be a surprisingly useful scripting language.

After I used the Flickr API library for Javascript to cross-reference my Flickr archive with my blog posts, I looked around for other things I could do with it. photoSync occasionally didn’t upload new pictures I added to its folders (or at least, not as quickly as I wanted). I wanted to replace photoSync with my own script that would:

  • upload the picture only if it doesn’t already exist,
  • add tags based on the filename,
  • add the photo to my Sketchbook photoset,
  • move the photo to the “To blog” folder, and
  • make it easy for me to refer to the Flickr image in my blog post or index.

The flickr-with-uploads library made it easy to upload images and retrieve information, although the format was slightly different from the Flickr API library I used previously. (In retrospect, I should’ve checked the Flickr API documentation first – there’s an example upload request right on the main page. Oh well! Maybe I’ll change it if I feel like rewriting it.)

I searched my existing photos to see if a photo with that title already existed. If it did, I displayed an Org-style list item with a link. If it didn’t exist, I uploaded it, set the tags, added the item to the photo set, and moved it to the folder. Then I displayed an Org-style link, but using a plus character instead of a minus character, taking advantage of the fact that both + and – can be used for lists in Org.

While using console.log(...) to display these links in the terminal allowed me to mark and copy the link, I wanted to go one step further. Could I send the links directly to Emacs? I looked into getting org-protocol to work, but I was having problems figuring this out. (I solved those problems; details later in this post.)

What were some other ways I could get the information into Emacs aside from copying and pasting from the terminal window? Maybe I could put text directly into the clipboard. The node-clipboard package didn’t build for me and I couldn’t get node-copy-paste to work either,about the node-copy-paste README told me about the existence of the clip command-line utility, which worked for me.

On Windows, clip allows you to pipe the output of commands into your clipboard. (There are similar programs for Linux or Mac OS X.) In Node, you can start a child process and communicate with it through pipes.

I got a little lost trying to figure out how to turn a string into a streamable object that I could set as the new standard input for the clip process I was going to spawn, but the solution turned out to be much simpler than that. Just write(...) to the appropriate stream, and call end() when you’re done.

Here’s the relevant bit of code that takes my result array and puts it into my clipboard:

var child = cp.spawn(‘clip’); child.stdin.write(result.join(“\n”)); child.stdin.end();

Of course, to get to that point, I had to revise my script. Instead of letting all the callbacks finish whenever they wanted, I needed to be able to run some code after everything was done. I was a little familiar with the async library, so I used that. I copied the output to the clipboard instead of displaying it so that I could call it easily using ! (dired-do-shell-command) and get the output in my clipboard for easy yanking elsewhere, although I could probably change my batch file to pipe the result to clip and just separate the stderr stuff. Hmm. Anyway, here it is!

See this on Github

/**
 * Upload the file to my Flickr sketchbook and then move it to
 * Dropbox/Inbox/To blog. Save the Org Mode links in the clipboard. -
 * means the photo already existed, + means it was uploaded.
 */

var async = require('async');
var cp = require('child_process');
var fs = require('fs');
var glob = require('glob');
var path = require('path');
var flickr = require('flickr-with-uploads');
var secret = require("./secret");
var SKETCHBOOK_PHOTOSET_ID = '72157641017632565';
var BLOG_INBOX_DIRECTORY = 'c:\\sacha\\dropbox\\inbox\\to blog\\';
var api = flickr(secret.flickrOptions.api_key,
                 secret.flickrOptions.secret,
                 secret.flickrOptions.access_token,
                 secret.flickrOptions.access_token_secret);
var result = [];

function getTags(filename) {
  var tags = [];
  var match;
  var re = new RegExp('#([^ ]+)', 'g');
  while ((match = re.exec(filename)) !== null) {
    tags.push(match[1]);
  }
  return tags.join(' ');
}
// assert(getTags("foo #bar #baz qux") == "bar baz");

function checkIfPhotoExists(filename, doesNotExist, existsFunction, done) {
  var base = path.basename(filename).replace(/.png$/, '');
  api({method: 'flickr.photos.search',
       user_id: secret.flickrOptions.user_id,
       text: base},
      function(err, response) {
        var found = undefined;
        if (response && response.photos[0].photo) {
          for (var i = 0; i < response.photos[0].photo.length; i++) {
            if (response.photos[0].photo && response.photos[0].photo[i]['$'].title == base) {
              found = i; break;
            }            
          }
        }
        if (found !== undefined) {
          existsFunction(response.photos[0].photo[found], done);
        } else {
          doesNotExist(filename, done);
        }
      });
}

function formatExistingPhotoAsOrg(photo, done) {
  var title = photo['$'].title;
  var url = 'https://www.flickr.com/photos/'
        + photo['$'].owner
        + '/' + photo['$'].id;
  result.push('- [[' + url + '][' + title + ']]');
  done();
}

function formatAsOrg(response) {
  var title = response.photo[0].title[0];
  var url = response.photo[0].urls[0].url[0]['_'];
  result.push('+ [[' + url + '][' + title + ']]');
}

function uploadImage(filename, done) {
  api({
    method: 'upload',
    title: path.basename(filename.replace(/.png$/, '')),
    is_public: 1,
    hidden: 1,
    safety_level: 1,
    photo: fs.createReadStream(filename),
    tags: getTags(filename.replace(/.png$/, ''))
  }, function(err, response) {
    if (err) {
      console.log('Could not upload photo: ', err);
      done();
    } else {
      var newPhoto = response.photoid[0];
      async.parallel(
        [
          function(done) {
            api({method: 'flickr.photos.getInfo',
                 photo_id: newPhoto}, function(err, response) {
                   if (response) { formatAsOrg(response); }
                   done();
                 });
          },
          function(done) {
            api({method: 'flickr.photosets.addPhoto',
                 photoset_id: SKETCHBOOK_PHOTOSET_ID,
                 photo_id: newPhoto}, function(err, response) {
                   if (!err) {
                     moveFileToBlogInbox(filename, done);
                   } else {
                     console.log('Could not add ' + filename + ' to Sketchbook');
                     done();
                   }
                 });
          }],
        function() {
          done();
        });
    }
  });
}

function moveFileToBlogInbox(filename, done) {
  fs.rename(filename, BLOG_INBOX_DIRECTORY + path.basename(filename),
            function(err) {
              if (err) { console.log(err); }
              done();
            });
}

var arguments = process.argv.slice(2);
async.each(arguments, function(item, done) {
  if (item.match('\\*')) {
    glob.glob(item, function(err, files) {
      if (!files) return;
      async.each(files, function(file, done) {
        checkIfPhotoExists(file, uploadImage, formatExistingPhotoAsOrg, done);
      }, function() {
        done();
      });
    });
  } else {
    checkIfPhotoExists(item, uploadImage, formatExistingPhotoAsOrg, done);
  }
}, function(err) {
  console.log(result.join("\n"));
  var child = cp.spawn('clip');
  child.stdin.write(result.join("\n"));
  child.stdin.end();
});

Wheeee! Hooray for automation. I made a Windows batch script like so:

up.bat

node g:\code\node\flickr-upload.js %*

and away I went. Not only did I have a handy way to process images from the command line, I could also mark the files in Emacs Dired with m, then type ! to execute my up command on the selected images. Mwahaha!

Anyway, I thought I’d write it up in case other people were curious about using Node to code little utilities, filling the clipboard in Windows, or getting data back into Emacs (sometimes the clipboard is enough).

Back to org-protocol, since I was curious about it. With (require 'org-protocol) (server-start), emacsclient org-protocol://store-link:/foo/bar worked when I entered it at the command prompt. I was having a hard time getting it to work under Node, but eventually I figured out that:

  • I needed to pass -n as one of the arguments to emacsclient so that it would return right away.
  • The : after store-link is important! I was passing org-protocol://store-link/foo/bar and wondering why it opened up a file called bar. org-protocol://store-link:/foo/bar was what I needed.

I only just figured out that last bit while writing this post. Here’s a small demonstration program:

var cp = require('child_process');
var child = cp.execFile('emacsclient', ['-n', 'org-protocol://store-link:/foo/bar']);

Yay!

2015-01-13 Using Node as a scripting tool -- index card #javascript #nodejs #coding #scripting

2015-01-13 Using Node as a scripting tool – index card #javascript #nodejs #coding #scripting

Org Mode: Reusing the date from file-datetree-prompt

Update 2015-02-17: Or you can just use %t in your org-capture-templates, as Seth Mason points out in the comments… =)

How can you get Org Mode to create and schedule entries within a year-month-day outline structure? You can define an org-capture-templates with the keyword file+datetree+prompt. This lets you specify a date for your entry, and Org will create the entry in a hierarchy organized by year, month, and day.

If you’d like to display the entry in your agenda, you’ll also need an active timestamp of the form <yyyy-mm-dd>. Fortunately, you can reuse the date you specified at the initial prompt to create the datetree entry. Looking at org-capture.el will show you that the org-capture function refers to the org-read-date-final-answer, which is set to whatever string you entered at the date prompt. For example, if you entered 18, then org-read-date-final-answer will be set to 18. You can use org-read-date to convert this back to a yyyy-mm-dd-style date.

How do you use this in org-capture-templates? You can use the %(...) syntax for calling an Emacs Lisp expression, like so:

(setq org-capture-templates '(
  ;; other entries go here
  ("s" "Journal entry with date, scheduled" entry
   (file+datetree+prompt "~/personal/journal.org")
    "* %^{Title}\n<%(org-read-date nil nil org-read-date-final-answer)>\n%i\n%?\n")))

Here’s sample output from that capture template:

* 2015
** 2015-12 December
*** 2015-12-31 Thursday
**** End of the year party!
<2015-12-31>

Thanks to Sean Miller for the nudge to think about this!

Continuous integration and code coverage for Emacs packages with Travis and Coveralls

Do you maintain an Emacs package hosted on Github? Would you like to get those confidence-building, bragging-rights-granting, other-developers-inspiring build: passing and coverage: 100% badges into your README file?

It turns out that this is pretty easy with ERT, Cask, Travis CI, undercover.el, and Coveralls.io.

  1. Log on to Travis and enable continuous integration for your repository.
  2. Log on to Coveralls.io and enable coverage testing for your repository.
  3. Set up a git branch, since you’ll probably be making lots of small commits while you smooth out the testing workflow.
  4. Define your tests with ERT. See https://github.com/abo-abo/tiny/blob/master/tiny-test.el for an example. For undercover support, you’ll want to include something like:
    (when (require 'undercover nil t)
      (undercover "tiny.el"))
    
  5. Define your dependencies with Cask. Include undercover. For example, here’s a simple Cask file:
    (source gnu)
    (source melpa)
    
    (development
      (depends-on "undercover"))
    
  6. Add a .travis.yml that specifies how to test your package on Travis. For example, see this .travis.yml and Makefile.
  7. Commit and push.
  8. Check your repository status in Travis to see if it ran properly.
  9. Check your coverage status in Coveralls.io to see if it displayed properly.
  10. Get the badge code from Travis and Coveralls, and add them to your README (probably using Markdown). You can get the badge code from Travis by clicking on your build status badge next to your repository name. Coveralls has prominent instructions for getting your badge. Yay!

Incidentally, if you want to see your test coverage locally, you can (require 'testcover) and then use testcover-this-defun or testcover-start to instrument the macros and functions for coverage. Run your tests, then use testcover-mark-all to look at the results. See the documentation in testcover.el to find out what the coloured overlays mean. Edebug has a test coverage tool too, so you can explore that one if you prefer it.

Additional notes on testing:

2015-02-03 Better Emacs Testing -- index card #testing #emacs

2015-02-03 Better Emacs Testing – index card #testing #emacs

2015-02-04 Yay, testing in Emacs -- index card #testing #emacs

2015-02-04 Yay, testing in Emacs – index card #testing #emacs

Resources: