davideisinger.com

My personal website
Log | Files | Refs | README

leancrew-com-7l5uqs.txt (11711B)


      1 [snowman-20]
      2 
      3 [1]And now it’s all this
      4 
      5 I just said what I said and it was wrong
      6 Or was taken wrong
      7 
      8 [2]Next post [3]Previous post
      9 
     10 [4]Tidying Markdown reference links
     11 
     12 September 17, 2012 at 9:15 PM by Dr. Drang
     13 
     14 Oscar Wilde—who would have been great on Twitter—[5]said “I couldn’t help it. I
     15 can resist everything except temptation.” That’s my excuse for this post.
     16 
     17 Several days ago I got an email from a reader, asking if I knew of a script
     18 that would tidy up [6]Markdown reference links in a document. She wanted them
     19 reordered and renumbered at the end of the document to match the order in which
     20 they appear in the body of the text. I didn’t know of one^[7]1 and suggested
     21 she write it herself and let me know when it’s done. I’ve been getting progress
     22 reports, but her script isn’t finished yet.
     23 
     24 There’s certainly no need to tidy the links up that way. Markdown doesn’t care
     25 what order the reference links appear in or the labels that are assigned to
     26 them. I’ve written dozens of posts in which the order of the references at the
     27 end of the Markdown source were way off from the order of the links in body.
     28 But…
     29 
     30 But there is an attraction to putting everything in apple pie order, even when
     31 no one but me will ever see it. Last night I succumbed and wrote a script to
     32 tidy up the links. Sorry, Phaedra.
     33 
     34 Here’s an example of a short Markdown document with out-of-order reference
     35 links:
     36 
     37 Species and their hybrids, How simply are these facts! How
     38 strange that the pollen of each But we may thus have
     39 [succeeded][2] in selecting so many exceptions to this rule.
     40 but the species would not all the same species living on the
     41 White Mountains, in the arctic regions of that large island.
     42 The exceptions which are now large, and triumphant, and
     43 which are known to every naturalist: scarcely a single
     44 [character][4] in the descendants of the Glacial period,
     45 would have been of use to the plants, have been accumulated
     46 and if, in both regions.
     47 
     48 Supposed to be extinct and unknown, form. We have seen that
     49 it yields readily, when subjected as [under confinement][3],
     50 to new and improved varieties will have been much
     51 compressed, we may assume that the species, which are
     52 already present in the ordinary spines serve as a prehensile
     53 or snapping apparatus. Thus every gradation, from animals
     54 with true lungs are descended from a marsupial form), "and
     55 if so, there can be followed by which viscid matter, such as
     56 that of making [slaves][1]. Let it be remembered that
     57 selection may be extended--to the stigma of.
     58 
     59 [1]: http://daringfireball.net/markdown/
     60 [2]: http://www.google.com/
     61 [3]: http://docs.python.org/library/index.html
     62 [4]: http://www.kungfugrippe.com/
     63 
     64 Note that the references are numbered 1, 2, 3, 4 at the bottom of the document,
     65 but that they appear in the body in the order 2, 4, 3, 1. The purpose of the
     66 script is to change the document to
     67 
     68 Species and their hybrids, How simply are these facts! How
     69 strange that the pollen of each But we may thus have
     70 [succeeded][1] in selecting so many exceptions to this rule.
     71 but the species would not all the same species living on the
     72 White Mountains, in the arctic regions of that large island.
     73 The exceptions which are now large, and triumphant, and
     74 which are known to every naturalist: scarcely a single
     75 [character][2] in the descendants of the Glacial period,
     76 would have been of use to the plants, have been accumulated
     77 and if, in both regions.
     78 
     79 Supposed to be extinct and unknown, form. We have seen that
     80 it yields readily, when subjected as [under confinement][3],
     81 to new and improved varieties will have been much
     82 compressed, we may assume that the species, which are
     83 already present in the ordinary spines serve as a prehensile
     84 or snapping apparatus. Thus every gradation, from animals
     85 with true lungs are descended from a marsupial form), "and
     86 if so, there can be followed by which viscid matter, such as
     87 that of making [slaves][4]. Let it be remembered that
     88 selection may be extended--to the stigma of.
     89 
     90 
     91 [1]: http://www.google.com/
     92 [2]: http://docs.python.org/library/index.html
     93 [3]: http://www.kungfugrippe.com/
     94 [4]: http://daringfireball.net/markdown/
     95 
     96 Now the links are numbered 1, 2, 3, 4 in both the text and the end references.
     97 The HTML produced when this document is run through a Markdown processor will
     98 be the same as the previous one—the links will still go to the right places—but
     99 the Markdown source looks better.
    100 
    101 Here’s the script that does it:
    102 
    103 python:
    104  1:  #!/usr/bin/python
    105  2:
    106  3:  import sys
    107  4:  import re
    108  5:
    109  6:  '''Read a Markdown file via standard input and tidy its
    110  7:  reference links. The reference links will be numbered in
    111  8:  the order they appear in the text and placed at the bottom
    112  9:  of the file.'''
    113 10:
    114 11:  # The regex for finding reference links in the text. Don't find
    115 12:  # footnotes by mistake.
    116 13:  link = re.compile(r'\[([^\]]+)\]\[([^^\]]+)\]')
    117 14:
    118 15:  # The regex for finding the label. Again, don't find footnotes
    119 16:  # by mistake.
    120 17:  label = re.compile(r'^\[([^^\]]+)\]:\s+(.+)$', re.MULTILINE)
    121 18:
    122 19:  def refrepl(m):
    123 20:    'Rewrite reference links with the reordered link numbers.'
    124 21:    return '[%s][%d]' % (m.group(1), order.index(m.group(2)) + 1)
    125 22:
    126 23:  # Read in the file and find all the links and references.
    127 24:  text = sys.stdin.read()
    128 25:  links = link.findall(text)
    129 26:  labels = dict(label.findall(text))
    130 27:
    131 28:  # Determine the order of the links in the text. If a link is used
    132 29:  # more than once, its order is its first position.
    133 30:  order = []
    134 31:  for i in links:
    135 32:    if order.count(i[1]) == 0:
    136 33:      order.append(i[1])
    137 34:
    138 35:  # Make a list of the references in order of appearance.
    139 36:  newlabels = [ '[%d]: %s' % (i + 1, labels[j]) for (i, j) in enumerate(order) ]
    140 37:
    141 38:  # Remove the old references and put the new ones at the end of the text.
    142 39:  text = label.sub('', text).rstrip() + '\n'*3 + '\n'.join(newlabels)
    143 40:
    144 41:  # Rewrite the links with the new reference numbers.
    145 42:  text = link.sub(refrepl, text)
    146 43:
    147 44:  print text
    148 
    149 The regular expressions in Lines 13 and 17 are fairly easy to understand. The
    150 first one looks for the links in the body of the text and the second looks for
    151 the labels.
    152 
    153 The key to the script are the four data structures: links, labels, order, and
    154 newlabels. For our example document, links is the list of tuples
    155 
    156 [('succeeded', '2'),
    157  ('single character', '4'),
    158  ('under confinement', '3'),
    159  ('slaves', '1')]
    160 
    161 labels is the dictionary
    162 
    163 {'1': 'http://daringfireball.net/markdown/',
    164  '3': 'http://docs.python.org/library/index.html',
    165  '2': 'http://www.google.com/',
    166  '4': 'http://www.kungfugrippe.com/'}
    167 
    168 order is the list
    169 
    170 ['2', '4', '3', '1']
    171 
    172 and newlabels is the list of strings
    173 
    174 ['[1]: http://www.google.com/',
    175  '[2]: http://docs.python.org/library/index.html',
    176  '[3]: http://www.kungfugrippe.com/',
    177  '[4]: http://daringfireball.net/markdown/']
    178 
    179 links and labels are built via the regex findall method in Lines 25-26. links
    180 is the direct output of the method and maintains the order in which the links
    181 appear in the text. labels is that same output, but converted to a dictionary.
    182 Its order, which we don’t care about, is lost in the conversion, but it can be
    183 used to easily access the URL from the link label.
    184 
    185 order is the order in which the link labels first appear in the text. The if
    186 statement in Line 32 ensures that repeated links don’t overwrite each other.
    187 
    188 newlabels is built from labels and order in Line 36. It’s the list of labels
    189 after the renumbering. Line 39 deletes the original label lines and puts the
    190 new ones at the end of the document.
    191 
    192 Finally, Line 42 replaces all the link labels in the body of the text with the
    193 new values. Rather than a replacement string, it uses a simple replacement
    194 function defined in Lines 19-21 to do so.
    195 
    196 Barring any bugs I haven’t found yet, this script (or filter) will work on any
    197 Markdown document and can be used either directly from the command line or
    198 through whatever system your text editor uses to call external scripts. I have
    199 it stored in BBEdit’s Text Filters folder under the name “Tidy Markdown
    200 Reference Links.py,” so I can call it from the Text ‣ Apply Text Filter
    201 submenu.
    202 
    203 I should mention that although this script is fairly compact and simple, it
    204 didn’t spring from my head fully formed. There were starts and stops as I
    205 figured out which data structures were needed and how they could be built. Each
    206 little subsection of the script was tested as I went along. The order list was
    207 originally a list of tuples; it wasn’t until I had a working version of the
    208 entire script that I realized that it could be simplified down to a list of
    209 link labels. That change shortened the script by five lines or so and, more
    210 importantly, clarified its logic.
    211 
    212 Despite these improvements, the script is hardly foolproof. The Markdown source
    213 of this very post confuses the hell out it. Not only does it think there are
    214 links in the sample document (which you’d probably guess), it also thinks the
    215 [%s][%d] in Line 21 of the script is a link (and the one in this sentence,
    216 too). And why wouldn’t it? To distinguish between real links and things that
    217 look like links in embedded source code, the script would have to be able to
    218 parse Markdown, not just match a couple of short regular expressions. This is a
    219 variant on what Hamish Sanderson said in the comments on [8]an earlier post.
    220 
    221 At the moment, I’m not willing to sacrifice the simplicity of the Tidy script
    222 to get it to handle weird posts like this one. But if I find that it fails
    223 often with the kind of input I commonly give it, I’ll have to revisit that
    224 decision.
    225 
    226 As Wilde also said, “Experience is the name everyone gives to their mistakes.”
    227 
    228 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    229 
    230  1. I didn’t think [9]Seth Brown’s formd did that, but [10]this tweet from
    231     Brett Terpsta says I was wrong about that. [11]↩
    232 
    233 [12]Next post [13]Previous post
    234 
    235 Site search
    236 
    237 [21][                    ] [22][Go!]
    238 Meta
    239 
    240   • drdrang at leancrew
    241   • [23]Blog archive
    242   • [24]RSS feed
    243   • [25]JSON feed
    244   • [26]Mastodon
    245   • [27]GitHub repositories
    246 
    247 Recent posts
    248 
    249 Credits
    250 
    251 [28] Powered by MathJax
    252 
    253 This work is licensed under a [29]Creative Commons Attribution-Share Alike 3.0
    254 Unported License.
    255 
    256 © 2005–2023, Dr. Drang
    257 
    258 
    259 References:
    260 
    261 [1] https://leancrew.com/all-this/
    262 [2] https://leancrew.com/all-this/2012/09/some-kind-of-druid-dudes-lifting-the-veil/
    263 [3] https://leancrew.com/all-this/2012/09/implementing-pubsubhubbub/
    264 [4] https://leancrew.com/all-this/2012/09/tidying-markdown-reference-links/
    265 [5] http://www.gutenberg.org/dirs/etext97/lwfan10h.htm
    266 [6] http://daringfireball.net/projects/markdown/syntax#link
    267 [7] https://leancrew.com/all-this/2012/09/tidying-markdown-reference-links/#fn:formd
    268 [8] http://www.leancrew.com/all-this/2012/09/applescript-syntax-highlighting-finally/
    269 [9] http://www.drbunsen.org/formd-a-markdown-formatting-tool.html
    270 [10] https://twitter.com/ttscoff/status/247398632377184256
    271 [11] https://leancrew.com/all-this/2012/09/tidying-markdown-reference-links/#fnref:formd
    272 [12] https://leancrew.com/all-this/2012/09/some-kind-of-druid-dudes-lifting-the-veil/
    273 [13] https://leancrew.com/all-this/2012/09/implementing-pubsubhubbub/
    274 [23] https://leancrew.com/all-this/archive/
    275 [24] https://leancrew.com/all-this/feed/
    276 [25] https://leancrew.com/all-this/feed.json
    277 [26] https://fosstodon.org/@drdrang
    278 [27] http://github.com/drdrang
    279 [28] http://www.mathjax.org/
    280 [29] http://creativecommons.org/licenses/by-sa/3.0/