Usually when I'm writing posts I need to put references which most of the time is a simple link to some website. What was bugging me is that I write Markdown using Vim. This in turn means that to find a link, which I already know most of the time, I need to change to the browser search for it, copy and paste it back on my post, lots of work. The consequence is that I end up balancing the links that will be added to the post and maybe omitting ones just because I'm too lazy to switch contexts. So I decided to test a hypothesis, "What if I could Google search inside Vim and paste the link right from there?"*

Performing a Google search on the command line

To achieve this first I need a way to trigger a Google search using the command line. It turns out that is simple but it requires some setting up. Most is documented on this guide.

Using this guide I came up with this script to perform searches on the command line:

#!/usr/bin/env bash

GOOGLE_SEARCH_URL="https://www.googleapis.com/customsearch/v1?"
GOOGLE_SEARCH_ENGINE="cx=$(keychain google-customsearch-engine-id)"
GOOGLE_DEVELOPER_API_KEY="key=$(keychain google-customsearch-api-key)"

SEARCH_URL="${GOOGLE_SEARCH_URL}${GOOGLE_SEARCH_ENGINE}&${GOOGLE_DEVELOPER_API_KEY}"

[[ -z ${@} ]] && echo "Usage: googlesearch TERMS" && exit 1;

SEARCH_TERMS="q=$(echo -n ${@} | urlencode)"

curl --silent "${SEARCH_URL}&${SEARCH_TERMS}"

You might notice the keychain call which is this script, what it does is just fetch some confidential information from my OSX KeyChain, I used a similar idea when automating token retrieval to protect the pin number.

Also there is a urlencode to encode the search terms, it's a simple Perl script that you can find here, for some reason I didn't find a command line utility for this.

If you follow the guide and run this script you will get a JSON response from Google, I've opted not to filter the results on this script and use jq later making the script more flexible for future opportunities.

Wiring the search to Vim and Selecta

Integrating with Vim was a little bit harder, mostly because I wasn't sure how to insert in place on Vim. After learning about the expression register things got easier. First the Selecta function with some changes:

function! SelectaCommand(choice_command)
  try
    silent let selection = system(a:choice_command . " | selecta ")
  catch /Vim:Interrupt/
    " Swallow the ^C so that the redraw below happens; otherwise there will be
    " leftovers from selecta on the screen
    redraw!
    return ""
  endtry
  redraw!
  return selection
endfunction

I had to change a little bit the original Selecta function, I took out the Vim command parameters and made the function more simple. This way the function only returns what was selected and you can act on it afterwards.

Now the Vim function that creates the command which will be filtered by Selecta:

" Request user input and search google filtered by selecta
function! SearchGoogleSelecta()
  call inputsave()
  let search = input("Search Google for: ")
  call inputrestore()
  let search_cmd = "googlesearch " . search ." | jq '.items[].link' | tr -d '\"' "
  return substitute(SelectaCommand(search_cmd), '\n$', '', '')
endfunction

First this function has a standard way to ask for user input in Vim, plugins do this frequently. Whatever I type on 'Search Google for:' prompt will be stored in search. The search command is just a shell command, like:

googlesearch SEARCH_TERMS | jq '.items[].link' | tr -d '"'

Which will return the ten resulting links from Google, you also could get more information by using a different jq filter and later processing it, it is up to you how to leverage this. Lastly tr just remove the quotes from the links.

And finally, the last piece, the Vim mapping which uses the expression register to input the text.

" search google for links and filter results through selecta
imap <c-l> <c-r>=SearchGoogleSelecta()<cr>

With all done and hopefully working you can add links from a Google search directly in place using Vim. I have recorded a demo using asciinema you can check how it looks like here.

__*__ Tip in 2014 try to test your hypothesis more than create features.