{"id":13487,"date":"2015-12-30T16:08:56","date_gmt":"2015-12-30T16:08:56","guid":{"rendered":"https:\/\/www.bartbusschots.ie\/s\/?p=13487"},"modified":"2016-01-08T16:19:32","modified_gmt":"2016-01-08T16:19:32","slug":"pretty-markdown-html-links-with-text-expander-perl","status":"publish","type":"post","link":"https:\/\/www.bartbusschots.ie\/s\/2015\/12\/30\/pretty-markdown-html-links-with-text-expander-perl\/","title":{"rendered":"Pretty Markdown &#038; HTML Links With TextExpander &#038; Perl"},"content":{"rendered":"<p>What&#8217;s the problem to be solved? When doing show notes for the various podcasts I contribute to, I generally have to include a lot of links. Particularly for <a href=\"http:\/\/lets-talk.ie\/apple\" target=\"_blank\">Let&#8217;s Talk Apple<\/a> and the <em>Security Stuff<\/em> segment on the <a href=\"http:\/\/www.podfeet.com\/\" target=\"_blank\">Nosillacast<\/a>, I want to clearly show where the news story came from. I also want to keep the link text short. To balance those two requirements, I decided to use the domain part of the URL with <code>\/...<\/code> appended to it as the link text. Doing this manually would be a really big pain in the backside because there are often more than 50 links in the notes for a single episode! Clearly, this needed automation.<\/p>\n<p>I do my show notes in <a href=\"https:\/\/en.wikipedia.org\/wiki\/Markdown\" target=\"_blank\">Markdown<\/a>, so my initial solution produced links in Markdown format. Allison asked me how I did my links, because she thought they looked good, and she assumed I would have automated the process in some way. I explained my solution, and she asked if I could build a variant for her that generates the same style of links in HTML format.<\/p>\n<p>My solution is built using a number of building blocks, but the glue that holds it all together is <a href=\"https:\/\/smilesoftware.com\/textexpander\" target=\"_blank\">TextExpander<\/a>. TextExpander is a paid-for Mac-only app, but one I would argue is well worth the price of admission. TextExpander is one of the first things I install on every new Mac.<\/p>\n<p>TextExpander&#8217;s raison d&#8217;\u00c3\u00aatre is to monitor everything you type, looking for abbreviations you have defined, and replacing them with expansions you have defined as you type. Each of these abbreviations\/expansion pairs are referred to as <em>snippets<\/em> within TextExpander.<\/p>\n<p>Snippets can be very simple replacements like expanding <code>*bb<\/code> into <code>Bart Busschots<\/code>, but they can also be much more complex. The next level up would be to include special codes to inject data into the replacements, for example, I have a snippet that replaces <code>*di<\/code> with the current date and my initials, the content of that snippet is <code>%d-%m-%Y BB<\/code> &#8211; <code>%d<\/code> gets replaced with the day part of the current date, <code>%m<\/code> the month part of the current date etc.. The next level of complexity is that you can include special codes to make TextExpander popup a little form when ever you type the abbreviation into which you then fill in information that gets included in the replacement text.<\/p>\n<p>However, the thing that really makes TextExpander powerful in my mind is it&#8217;s ability to execute a script to generate the replacement text. The scripts can be written in AppleScript, JavaScript, and any scripting language supported in the OS X terminal. This allows TextExpander to utilise the full power of the Terminal, as well as the full power of scripting languages like Perl, Python and Ruby.<\/p>\n<p>Unsurprisingly, I like to use Perl in my snippets, and in this case, I use Perl to reach out to the <code>pbpaste<\/code> terminal command, and to harness the power of the <code>URI<\/code> perl module.<\/p>\n<p>I use my solution as follows:<\/p>\n<ol>\n<li>Copy the link to be included in the show notes to the clipboard<\/li>\n<li><code>Cmd+tab<\/code> to the show notes I am working on and type the abbreviation <code>*mdlc<\/code> into the place in the notes where I want the URL to be inserted (all my Markdown expansions start with <code>*md<\/code>, and <code>lc<\/code> stands for <em>linkify clipboard<\/em>).<\/li>\n<li>The TextExpander snippet then executes &#8211; it fetches the URL from the clipboard, parses it with the <code>URI<\/code> perl module, and assuming that succeeds, generates the Markdown code for the link, and inserts it into the show notes in place of the <code>*mdlc<\/code> abbreviation.<\/li>\n<\/ol>\n<p><!--more--><\/p>\n<div class=\"podcast\">\n<p>Listen Along: <a href=\"http:\/\/www.podfeet.com\/blog\/2015\/12\/ccatp-419\/\" target=\"_blank\">Chit chat Across the Pond Episode 419<\/a><\/p>\n<!--[if lt IE 9]><script>document.createElement('audio');<\/script><![endif]-->\n<audio class=\"wp-audio-shortcode\" id=\"audio-13487-1\" preload=\"none\" style=\"width: 100%;\" controls=\"controls\"><source type=\"audio\/mpeg\" src=\"http:\/\/media.blubrry.com\/nosillacast\/traffic.libsyn.com\/nosillacast\/CCATP_2015_12_31.mp3?_=1\" \/><a href=\"http:\/\/media.blubrry.com\/nosillacast\/traffic.libsyn.com\/nosillacast\/CCATP_2015_12_31.mp3\">http:\/\/media.blubrry.com\/nosillacast\/traffic.libsyn.com\/nosillacast\/CCATP_2015_12_31.mp3<\/a><\/audio><\/div>\n<h2>Prep Work<\/h2>\n<p>For this solution to work, you obviously need TextExpander installed on your Mac. You also need the <code>URI<\/code> perl module installed. You can check if the module is installed with the command:<\/p>\n<pre class=\"crayon:false\">\r\nperl -e 'use URI; print \"OK\\n\";'\r\n<\/pre>\n<p>If the module is installed, the only output you&#8217;ll get is &#8216;OK&#8217;:<\/p>\n<pre class=\"crayon:false\">\r\nbart-iMac2013:~ bart$ perl -e 'use URI; print \"OK\\n\";'\r\nOK\r\nbart-iMac2013:~ bart$\r\n<\/pre>\n<p>If the module is not installed, you&#8217;ll get a big messy error message.<\/p>\n<p>To install the module simply run:<\/p>\n<pre class=\"crayon:false\">\r\nsudo cpan URI\r\n<\/pre>\n<h2>The Markdown TextExpander Snippet<\/h2>\n<p>When creating a new snippet in TextExpander, you can choose the content type for the snippet with a dropdown menu at the top of the snippet content text area. The default value for this dropdown is <code>Plain Text<\/code>, but we need to change it to <code>Shell Script<\/code>:<\/p>\n<p style=\"text-align:center\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.bartbusschots.ie\/s\/wp-content\/uploads\/2015\/12\/Screen-Shot-2015-12-30-at-15.13.39.png\" alt=\"TextExpander Script Screenshot\" width=\"480\" height=\"401\" class=\"alignnone size-full wp-image-13488\" style=\"border:0px;\" srcset=\"https:\/\/www.bartbusschots.ie\/s\/wp-content\/uploads\/2015\/12\/Screen-Shot-2015-12-30-at-15.13.39.png 480w, https:\/\/www.bartbusschots.ie\/s\/wp-content\/uploads\/2015\/12\/Screen-Shot-2015-12-30-at-15.13.39-300x251.png 300w\" sizes=\"auto, (max-width: 480px) 100vw, 480px\" \/><\/p>\n<p>This will auto-populate the content of the new snippet with some sample code. Replace this sample code with the following Perl code:<\/p>\n<pre class=\"lang:perl decode:true \" title=\"Pretty Markdown Links TextExpander Snippet\" >\r\n#!\/usr\/bin\/perl\r\n\r\nuse strict;\r\nuse warnings;\r\nuse URI;\r\n\r\nmy $clipboard = `\/usr\/bin\/pbpaste`;\r\n\r\nmy $url = URI-&gt;new($clipboard);\r\n\r\n# if we didn't get a valid URL, just call it a day\r\nunless($url-&gt;scheme() =~ m\/^https?$\/sx){\r\n\texit 1;\r\n}\r\n\r\n# assemble and print the markdown link\r\nprint q{[}.$url-&gt;host().q{\/...](}.$url-&gt;canonical().q{)};\r\n<\/pre>\n<p>So, what is going on here?<\/p>\n<p>The first line is the so-called shebang line, it tells TextExpander which interpreter to use to execute the script &#8211; in this case, Perl.<\/p>\n<p>Lines 3 &#038; 4 include standard best-practices Perl modules.<\/p>\n<p>Line 5 includes the <code>URI<\/code> module which we&#8217;ll use to interpret our URL.<\/p>\n<p>Line 7 shells out to the <code>pbpaste<\/code> command using Perl&#8217;s back-tick (reverse single quote) operator. The back-tick operator executes the terminal command enclosed between the back-ticks, and saves the output of that command into the variable <code>$clipboard<\/code>.<\/p>\n<p>Line 9 creates a <code>URI<\/code> object called <code>$url<\/code> from the content of the clipboard.<\/p>\n<p>Lines 12 through 14 check that the clipboard contained an absolute URL by checking that the URL scheme is either <code>http<\/code> or <code>https<\/code> by matching it against a regular expression.<\/p>\n<p>Finally, line 17 prints out the link in Markdown format. <code>$url->host()<\/code> returns just the domain part of the URL, and <code>$url->canonical()<\/code> returns the full absolute URL.<\/p>\n<p>A reminder that in Markdown, a URL is represented as follows:<\/p>\n<pre class=\"crayon:false\">\r\n[<u>LINK_TEXT<\/u>](<u>LINK_URL<\/u>)\r\n<\/pre>\n<p>So, Line 17 prints the character <code>[<\/code>, then the domain part of the URL, then the characters <code>\/...](<\/code>, then the URL, and finally the character <code>)<\/code>. Note that in Perl, a string can be enclosed between <code>q{<\/code> and <code>}<\/code>.<\/p>\n<h2>The HTML Variant<\/h2>\n<p>To convert this snippet to HTML, all we need to do is alter the line that does the printing, that is to say, line 17.<\/p>\n<p>A reminder that in HTML, a link is coded as follows:<\/p>\n<pre class=\"crayon:false\">\r\n&lt;a href=\"<u>LINK_URL<\/u>\" target=\"_blank\"&gt;<u>LINK_TEXT<\/u>&lt;\/a&gt;\r\n<\/pre>\n<p>So, the new line 17 is:<\/p>\n<pre class=\"lang:perl decode:true \" title=\"HTML Link Printing Code\" >\r\nprint q{&lt;a href=\"}.$url-&gt;canonical().q{\" target=\"_blank\"&gt;}.$url-&gt;host().q{\/...&lt;\/a&gt;};\r\n<\/pre>\n<p>For completeness, here is the full content for the HTML variant of the Snippet:<\/p>\n<pre class=\"lang:perl decode:true \" title=\"Pretty HTML Links TextExpander Snippet\" >\r\n#!\/usr\/bin\/perl\r\n\r\nuse strict;\r\nuse warnings;\r\nuse URI;\r\n\r\nmy $clipboard = `\/usr\/bin\/pbpaste`;\r\n\r\nmy $url = URI-&gt;new($clipboard);\r\n\r\n# if we didn't get a valid URL, just call it a day\r\nunless($url-&gt;scheme() =~ m\/^https?$\/sx){\r\n\texit 1;\r\n}\r\n\r\n# assemble and print the HTML link\r\nprint q{&lt;a href=\"}.$url-&gt;canonical().q{\" target=\"_blank\"&gt;}.$url-&gt;host().q{\/...&lt;\/a&gt;};\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>What&#8217;s the problem to be solved? When doing show notes for the various podcasts I contribute to, I generally have to include a lot of links. Particularly for Let&#8217;s Talk Apple and the Security Stuff segment on the Nosillacast, I want to clearly show where the news story came from. I also want to keep [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[543,12,16],"tags":[432,267,544,458],"series":[],"class_list":["post-13487","post","type-post","status-publish","format-standard","hentry","category-automation","category-computers-tech","category-programming","tag-html","tag-perl","tag-textexpander","tag-tutorial"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p7t9xK-3vx","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.bartbusschots.ie\/s\/wp-json\/wp\/v2\/posts\/13487","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.bartbusschots.ie\/s\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.bartbusschots.ie\/s\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.bartbusschots.ie\/s\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/www.bartbusschots.ie\/s\/wp-json\/wp\/v2\/comments?post=13487"}],"version-history":[{"count":6,"href":"https:\/\/www.bartbusschots.ie\/s\/wp-json\/wp\/v2\/posts\/13487\/revisions"}],"predecessor-version":[{"id":13558,"href":"https:\/\/www.bartbusschots.ie\/s\/wp-json\/wp\/v2\/posts\/13487\/revisions\/13558"}],"wp:attachment":[{"href":"https:\/\/www.bartbusschots.ie\/s\/wp-json\/wp\/v2\/media?parent=13487"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.bartbusschots.ie\/s\/wp-json\/wp\/v2\/categories?post=13487"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.bartbusschots.ie\/s\/wp-json\/wp\/v2\/tags?post=13487"},{"taxonomy":"series","embeddable":true,"href":"https:\/\/www.bartbusschots.ie\/s\/wp-json\/wp\/v2\/series?post=13487"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}