I recently ported a blog of mine from Python to Go (to improve speed and performance) and while all is great so far, I'd like some help optimising the Markdown
function to improve the general performance, maintenance and readability of the function.
I have this function because I write my blog articles in Markdown (.md
) and then use Python Go to convert the raw Markdown to HTML for output as this saves me from having to write ridiculous amounts of HTML. (which can be tedious to say the least)
The Markdown
function takes one argument (raw
) which is a string that contains the raw Markdown (obtained using ioutil.ReadFile
).
It then splits the Markdown by \n
(removing the empty lines) and converts:
- Bold and italic text (***,**,*)
- Strikethrough text (~~blah blah blah~~)
- Underscored text (__blah blah blah__)
- Links ([https://example.com](Example Link))
- Blockquotes (> sample quote by an important person)
- Inline code (`abcccc`)
- Headings (h1-h6)
While some of the supported features aren't exactly standard, this function works and outputs the expected result without any errors but being a new Go programmer and this being my first "real" Go project I'd like to know whether or not my code could be optimised for better performance, maintainability and readability.
Here a few questions I have regarding optimisation:
- Would it make a difference to performance if I reduced the amount of imports?
- Would it improve readability if I put the
regexp.MustCompile
functions into variables above theMarkdown
function? - Would it improve performance if I used individual regexes to convert Markdown headings instead of using
for i := 6; i >= 1; i-- {...}
? - If not, is there a way to convert
i
(an integer) to a string without usingstrconv.Itoa(i)
(to help reduce the amount of imports)?
Here is my code:
package parse
import (
"regexp"
"strings"
"strconv"
)
func Markdown(raw string) string {
// ignore empty lines with "string.Split(...)"
lines := strings.FieldsFunc(raw, func(c rune) bool {
return c == '\n'
})
for i, line := range lines {
// wrap bold and italic text in "<b>" and "<i>" elements
line = regexp.MustCompile(`\*\*\*(.*?)\*\*\*`).ReplaceAllString(line, `<b><i>$1</i></b>`)
line = regexp.MustCompile(`\*\*(.*?)\*\*`).ReplaceAllString(line, `<b>$1</b>`)
line = regexp.MustCompile(`\*(.*?)\*`).ReplaceAllString(line, `<i>$1</i>`)
// wrap strikethrough text in "<s>" tags
line = regexp.MustCompile(`\~\~(.*?)\~\~`).ReplaceAllString(line, `<s>$1</s>`)
// wrap underscored text in "<u>" tags
line = regexp.MustCompile(`__(.*?)__`).ReplaceAllString(line, `<u>$1</u>`)
// convert links to anchor tags
line = regexp.MustCompile(`\[(.*?)\]\((.*?)\)[^\)]`).ReplaceAllString(line, `<a href="$2">$1</a>`)
// escape and wrap blockquotes in "<blockquote>" tags
line = regexp.MustCompile(`^\>(\s|)`).ReplaceAllString(line, `>`)
line = regexp.MustCompile(`\>\;(.*?)$`).ReplaceAllString(line, `<blockquote>$1</blockquote>`)
// wrap the content of backticks inside of "<code>" tags
line = regexp.MustCompile("`(.*?)`").ReplaceAllString(line, `<code>$1</code>`)
// convert headings
for i := 6; i >= 1; i-- {
size, md_header := strconv.Itoa(i), strings.Repeat("#", i)
line = regexp.MustCompile(`^` + md_header + `(\s|)(.*?)$`).ReplaceAllString(line, `<h` + size + `>$2</h` + size + `>`)
}
// update the line
lines[i] = line
}
// return the joined lines
return strings.Join(lines, "\n")
}