1
\$\begingroup\$

I have a function which is used to convert text (a CHANGES file) into HTML for reading. The function is essentially line-based: if the line is of this type, do this thing; if some other type, do that thing; etc. Further logic needs to be added before the fallback

b.WriteString("<p>" + line + "</p>\n")

but I wanted to better organize my code before adding the more complex logic.

As written the code looks like a nightmare to me -- there has to be some better, more idiomatic way to write this. I'm new enough to Go that I'm not sure what the best way forward is.

var h3Regex = regexp.MustCompile("^Done for version ([1-9]\\.[0-9]\\.[0-9]) \\(released ([0-9/-]+)\\):$")
var h4Regex = regexp.MustCompile("^  (Fixed|Added|Changed|Removed)$")
var variable = "[a-zA-Z][a-zA-Z0-9_]*(?:\\(\\))?"
var stuff = "^  (?:[A-Z]+ )?[0-9]+- GP functions? (" + variable + "(?:, ?" + variable + ")*)$"
var GPRegex = regexp.MustCompile(stuff)

func layout(text string) string {
    lines := strings.Split(html.EscapeString(text), "\n")
    var b bytes.Buffer
    for _, line := range lines {
        match := h3Regex.FindStringSubmatch(line)
        if len(match) > 0 {
            b.WriteString("<h3>Version " + match[1] + ", released " + match[2] + "</h3>\n")
            continue
        }
        match = h4Regex.FindStringSubmatch(line)
        if len(match) > 0 {
            b.WriteString("<h4>" + match[1] + "</h4>\n")
            continue
        }
        match = GPRegex.FindStringSubmatch(line)
        if len(match) > 0 {
            funcs := strings.Split(match[1], ",")
            if len(funcs) > 1 {
                b.WriteString("<p>GP functions ")
            } else {
                b.WriteString("<p>GP function ")
            }
            for idx, f := range funcs {
                if idx > 0 {
                    b.WriteString(", ")
                }
                b.WriteString(`<code class="GP">` + f + `</code>`)
            }
            b.WriteString("</p>\n")
            continue
        }
        b.WriteString("<p>" + line + "</p>\n")
    }
    return b.String()
}
\$\endgroup\$
1
\$\begingroup\$

Take a look at html/template package from standard library. It will help you a lot in separating parsing code and HTML generation. Also it will escape all input by default.


Some other notes:

  • It is handy to use ` instead of " when writing regular expressions with lots of escapes.

  • GPRegex variable will be exported. Should it be?

  • Kudos for using bytes.Buffer and not concating strings by hand (:

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.