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/