Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert markdown links from inline to reference

I have a changelog file formatted using Github's markdown.

Initially I used inline links for every link I needed to add, that is:

This is some [example](http://www.stackoverflow.com) line of text.

Over time, as the file grew in size, it became a bit messy due mainly to this way of inserting links.

I'd like to convert all links from inline to reference (see description of each), that is convert the above line to this:

This is some [example][1] line of text.

[1]: http://www.stackoverflow.com

Since the file is rather large and contains many inline links, I was wondering if there is some automated way to do this. I use Sublime Text 3 to edit, but I couldn't find a suitable package for this task. Perhaps some clever regex?

like image 662
Gabriel Avatar asked Jun 18 '15 13:06

Gabriel


3 Answers

Came across this question thanks to Google. Maybe this can help others:

My answer isn't Sublime specific, but if you're using JavaScript (Node) already, I'd use a Markdown parser and CST transformer like remark.

For example, to transform all the inline links in README.md to numerically-ascending reference-style links, you could run the following at your project's root:

npm install --save-dev remark-cli remark-renumber-references
npx remark --no-stdout --output --use renumber-references README.md

Or, if you want reference-style links derived from the source uri:

npm install --save-dev remark-cli remark-defsplit
npx remark --no-stdout --output --use defsplit README.md

Hopefully this info helps people like me not waste a whole day hacking together some horrendously unreliable regexp-based solution to this :)

like image 179
Xunnamius Avatar answered Nov 10 '22 02:11

Xunnamius


That's a great requirement!

I've just created a new Node.js program (I know it's not a GUI but seems something more people would like the capability of) to do this on GitHub.

Here's also the code:

// node main.js test.md result.md

var fs = require('fs')
fs.readFile(process.argv[2], 'utf8', function (err, markdown) {
    if (err) {
        return console.log(err);
    }
    var counter = 1;
    var matches = {};
    var matcher = /\[.*?\]\((.*?)\)/g;
    while (match = matcher.exec(markdown)) {
        if (!matches[match[1]]) matches[match[1]] = counter++;
    }
    console.log(matches);
    Object.keys(matches).forEach(function(url) {
        var r = new RegExp("(\\[.*?\\])\\(" + url + "\\)", "g");
        markdown = markdown.replace(r, "$1[" + matches[url] + "]");
        markdown += "\n[" + matches[url] + "]: " + url;
    });

    fs.writeFile(process.argv[3], markdown, 'utf8', function (err) {
        if (err) return console.log(err);
    });

});
like image 9
bjfletcher Avatar answered Nov 10 '22 00:11

bjfletcher


Save this as mdrelink.py in your Packages folder, and you can then run it with

view.run_command('mdrelink');

from within the command console.

I think I got the order thingy right – reversing is necessary because otherwise it would mess up the already cached indexes of next items. It should also automatically skip already used link numbers. My first Python and my first Sublime plugin, so please be gentle with me.

import sublime, sublime_plugin

class mdrelinkCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        oldlinks = []
        self.view.find_all("^\s*(\[\d+\]):", sublime.IGNORECASE, "\\1", oldlinks)
        newlinkpos = self.view.find_all("\[.+?\](\(.+?\))")
        orgtext = []
        self.view.find_all("(\[.+?\])\(.+?\)", sublime.IGNORECASE, "\\1", orgtext)
        orglink = []
        self.view.find_all("\[.+?\]\((.+?)\)", sublime.IGNORECASE, "\\1", orglink)
        orglink.reverse()
        self.view.insert(edit, self.view.size(), '\n\n')
        counter = 1
        newnumbers = []
        for r in newlinkpos:
            while '['+str(counter)+']' in oldlinks:
                 counter += 1
            oldlinks.append('['+str(counter)+']')
            line = '[' + str(counter)+']: '+ orglink.pop() + '\n'
            newnumbers.append('  ['+str(counter)+']')
            self.view.insert(edit, self.view.size(), line)
        for r in reversed(newlinkpos):
            self.view.replace(edit, r, orgtext.pop()+newnumbers.pop())
like image 5
Jongware Avatar answered Nov 10 '22 01:11

Jongware