-
-
Save kopischke/1009149 to your computer and use it in GitHub Desktop.
| #!/usr/bin/env ruby -wKU | |
| # Adapted from Brett Terpstra’s original “Markdown to Evernote” service (http://brettterpstra.com/a-better-os-x-system-service-for-evernote-notes-with-multimarkdown/) | |
| # Martin Kopischke 2011 – License: Creative Commons Attribution Share-Alike (CC BY-SA) 3.0 Unported (http://creativecommons.org/licenses/by-sa/3.0/) | |
| # Changes: – create only one Evernote note per (Multi)Markdown input passed (instead of one per line) | |
| # – do not choke on shell escape characters (use Tempfile instead of shell pipe for osascript) | |
| # – default to MultiMarkdown 3 executable (instead of MMD 2 Perl script) | |
| # – make smart typography processing optional (set SMARTY to 'false' to bypass processing; | |
| # note smart typography cannot be disabled in MMD 3.0 and 3.0.1 | |
| # – handle both smart typography processing scripts (ie. SmartyPants.pl) | |
| # and (Multi)Markdown processor extensions (i.e. '--smart' and MMD's upcoming '--nosmart') | |
| # – handle both command line switches to (Multi)Markdown processor and separate smart typography processors | |
| # – restrict parsing for metadata (note title, tags, target notebook) to the metadata block as per MMD spec | |
| # note atx style 1st level headings will be parsed anywhere, as they terminate the metadata block anyway | |
| # – specify note title in metadata block MMD style, using 'Title: <name>' (not just as 1st level heading) | |
| # – specify target notebook in metadata block either with '= <name>' or MMD style, using 'Notebook: <name>' | |
| # – specify tags in metadata block either with '@ <tag list>' or MMD style, using 'Keywords: <tag list>' | |
| # – correctly parse tag names with punctuation (no commas) and other “special” characters | |
| # – correctly assign multiple tags (instead of quietly failing) | |
| # – correctly parse all flavors of 1st level atx headings (not just those following the hash with a space) | |
| # – use localized date time stamp from AppleScript (instead of US formatted) as fallback note title | |
| # – use Evernote default notebook if none is indicated in input (instead of 'Unfiled') | |
| # – added minimal sanity checks (like make sure Markdown executable actually exists and is executable) | |
| # – runs on Ruby 1.8 and 1.9 | |
| # To do: – process settext 1st level headings | |
| # – ignore Markdown 1st level headings when a 'Title:' metadata line is found? | |
| # Markdown executable path | |
| # – edit to match your install location if non-default | |
| # – pre-version 3 MMD script usually is '~/Application Support/MultiMarkDown/bin/MultiMarkDown.pl' | |
| MARKDOWN = '/usr/local/bin/multimarkdown' | |
| Process.exit unless File.executable?(MARKDOWN) | |
| # Smart typography (aka SmartyPants) switch | |
| SMARTY = true | |
| # – Smart typography processing via MARKDOWN extension | |
| # enable with '--smart' for PEG Markdown, disable using '--nosmart' in upcoming MMD version 3 | |
| SMARTY_EXT_ON = '--smart' | |
| SMARTY_EXT_OFF = '--nosmart' | |
| # – Separate smart typography processor (i.e. SmartyPants.pl) | |
| # set to path to SmartyPants.pl (for classic Markdown and MMD pre-version 3, usually same dir as (Multi)MarkDown.pl) | |
| # set to '' to use SMARTY_EXT instead | |
| SMARTY_PATH = '' | |
| if SMARTY && !SMARTY_PATH.empty? then Process.exit unless File.executable?(SMARTY_PATH) end | |
| # utility function: escape double quotes and backslashes (for AppleScript) | |
| def escape(str) | |
| str.to_s.gsub(/(?=["\\])/, '\\') | |
| end | |
| # utility function: enclose string in double quotes | |
| def quote(str) | |
| '"' << str.to_s << '"' | |
| end | |
| # buffer | |
| input = '' | |
| # processed data | |
| contents = '' | |
| title = '' | |
| tags = '' | |
| notebook = '' | |
| # operation switches | |
| metadata = true | |
| # parse for metadata and pass all non-metadata to input | |
| ARGF.each_line do |line| | |
| case line | |
| # note title (either MMD metadata 'Title' – must occur before the first blank line – or atx style 1st level heading) | |
| when /^Title:\s.*?/ | |
| if metadata then title = line[7..-1].strip else input << line end | |
| # strip all 1st level headings as logically, note title is 1st level | |
| when /^#[^#]+?/ | |
| if title.empty? then title = line[line.index(/[^#]/)..-1].strip end | |
| # note tags (either MMD metadata 'Keywords' or '@ <tag list>'; must occur before the first blank line) | |
| when /^(Keywords:|@)\s.*?/ | |
| if metadata then tags = line[line.index(/\s/)+1..-1].split(',').map {|tag| tag = tag.strip} else input << line end | |
| # notebook (either MMD metadata 'Notebook' or '= <name>'; must occur before the first blank line) | |
| when /^(Notebook:|=)\s.*?/ | |
| if metadata then notebook = line[line.index(/\s/)+1..-1].strip else input << line end | |
| # metadata block ends at first blank line | |
| when /^\s?$/ | |
| if metadata then metadata = false end | |
| input << line | |
| # anything else is appended to input | |
| else | |
| input << line | |
| end | |
| end | |
| # Markdown processing | |
| mmd_cmd = "#{quote MARKDOWN}" | |
| mmd_cmd << if SMARTY_PATH.empty? then SMARTY ? " #{SMARTY_EXT_ON}" : " #{SMARTY_EXT_OFF}" else "|#{quote SMARTY_PATH}" end unless !SMARTY | |
| IO.popen(mmd_cmd, 'r+') do |io| | |
| input.each_line {|line| io << line} | |
| io.close_write | |
| io.each_line {|line| contents << line} | |
| end | |
| # create note, using localized date and time stamp as fallback for title | |
| if title.empty? then title = %x{osascript -e 'get (current date) as text'}.chomp end | |
| osa_cmd = "tell application #{quote 'Evernote'} to create note with html #{quote escape contents}" | |
| osa_cmd << " title #{quote escape title}" | |
| if tags.length > 1 then osa_cmd << " tags #{'{' << tags.map {|tag| tag = quote escape tag}.join(",") << '}'}" end | |
| if tags.length == 1 then osa_cmd << " tags #{quote escape tags[0]}" end | |
| osa_cmd << " notebook #{quote escape notebook}" unless notebook.empty? | |
| require 'tempfile' | |
| Tempfile.open('md2evernote') do |file| | |
| file.puts osa_cmd | |
| file.rewind | |
| %x{osascript "#{file.path}"} | |
| end |
I created a modified version that uses RubyCocoa and ScriptingBridge instead of calling osascript via the command line. More robust to strange characters, less processing of input. Also changed some things for efficiency/simplicity.
Edits:
@christopherdwhite: My version was written on Mountain Lion, so should work there. Although I installed multimarkdown through brew instead of the packages on the site.
@svsmailus: You can pass a filename to the script, but passing multiple files will just result in one big note. I would suggest the following:
for f in (/path/to/dir/*); do ruby /path/to/gist "$f"; done
Props for the work!
I'm an avid user of it, but it is now broken under 10.9, and I have no clue if it's my workflow or if there is something to be changed in the code. I'm running it as a Sercive, built with Automator as a Service.
Any clue?
Added support in this fork for setting the creation date via the MMD metadata.
The applescript date class is used to do the text-->object conversion, which is pretty flexible, but no error handling is in place should if fail.
Has any one tested this on Mountain Lion? I can't seam to get it working. I've got the service in the right place, MMD installed with the support tools but when I use the service from a text editor it doesn't do anything.
Oddly, if I select a file in Path Finder and run the service it creates an Evernote note with the path and filename, it just won't work with selected text in Byword/FoldingText/Path Finder's text editor.