Add function to trim leading/trailing empty lines.

This commit is contained in:
Frank Dietrich 2022-07-21 13:15:23 +02:00
parent a23d372d1f
commit 9d2ae414b3
2 changed files with 107 additions and 1 deletions

View File

@ -3,10 +3,14 @@ package frontmatter
import (
"fmt"
"strings"
"unicode"
"unicode/utf8"
"gopkg.in/yaml.v1"
)
var asciiSpace = [256]uint8{'\t': 1, '\n': 1, '\v': 1, '\f': 1, '\r': 1, ' ': 1}
// Frontmatter encapsulates cheatsheet frontmatter data
type Frontmatter struct {
Tags []string
@ -24,7 +28,7 @@ func Parse(markdown string) (string, Frontmatter, error) {
// if the markdown does not contain frontmatter, pass it through unmodified
if !strings.HasPrefix(markdown, delim) {
return strings.TrimSpace(markdown), fm, nil
return TrimEmptyLines(markdown), fm, nil
}
// otherwise, split the frontmatter and cheatsheet text
@ -42,3 +46,52 @@ func Parse(markdown string) (string, Frontmatter, error) {
return strings.TrimSpace(parts[2]), fm, nil
}
// TrimEmptyLines returns a slice of the string s, with all leading
// and trailing lines removed, which consist only of white space as
// defined by Unicode.
func TrimEmptyLines(s string) string {
start := 0
newlinePos := -1
for ; start < len(s); start++ {
c := s[start]
if c >= utf8.RuneSelf {
return strings.TrimFunc(s[start:], unicode.IsSpace)
}
if c == '\n' {
newlinePos = start
continue
}
if asciiSpace[c] == 0 {
if newlinePos >= 0 {
start = newlinePos + 1
} else {
start = 0
}
break
}
}
stop := len(s)
newlinePos = -1
for ; stop > start; stop-- {
c := s[stop-1]
if c >= utf8.RuneSelf {
return strings.TrimFunc(s[start:stop], unicode.IsSpace)
}
if c == '\n' {
newlinePos = stop
continue
}
if asciiSpace[c] == 0 {
if newlinePos >= 0 {
stop = newlinePos
} else {
stop = len(s)
}
break
}
}
return s[start:stop]
}

View File

@ -93,3 +93,56 @@ To foo the bar: baz`
t.Errorf("failed to parse text: want: %s, got: %s", markdown, text)
}
}
// TestTrimEmptyLines assert that leading and trailing empty lines are removed
func TestTrimEmptyLines(t *testing.T) {
// define for readability of the tests
blank := "\x20"
linefeed := "\x0A"
testCases := []struct {
input string
want string
}{
// nothing to be trimmed
{"", ""},
{"a", "a"},
{" non empty line ", " non empty line "},
{" non empty line " + linefeed, " non empty line " + linefeed},
{" non empty line " + linefeed + " another non empty line ", " non empty line " + linefeed + " another non empty line "},
{" non empty line " + linefeed + " another non empty line " + linefeed, " non empty line " + linefeed + " another non empty line " + linefeed},
// trim leading empty lines
{linefeed + " non empty line ", " non empty line "},
{blank + linefeed + " non empty line ", " non empty line "},
{blank + linefeed + linefeed + " non empty line ", " non empty line "},
{linefeed + blank + linefeed + " non empty line ", " non empty line "},
{blank + linefeed + blank + linefeed + " non empty line ", " non empty line "},
{linefeed + " non empty line " + linefeed + " another non empty line ", " non empty line " + linefeed + " another non empty line "},
{blank, ""},
{linefeed, ""},
{blank + linefeed, ""},
{linefeed + blank, ""},
{blank + linefeed + blank, ""},
{linefeed + blank + linefeed, ""},
// trim trailing empty lines
{" non empty line " + linefeed + blank, " non empty line " + linefeed},
{" non empty line " + linefeed + blank + linefeed, " non empty line " + linefeed},
{" non empty line " + linefeed + blank + linefeed + blank, " non empty line " + linefeed},
}
for _, testCase := range testCases {
// parse the input
text, _, err := Parse(testCase.input)
// assert expectations
if err != nil {
t.Errorf("failed to parse input: %v", err)
}
// assert that the wanted output was returned
if text != testCase.want {
t.Errorf("failed to parse text: want: [%s], got: [%s]", testCase.want, text)
}
}
}