Skip to content

Commit ce4b06f

Browse files
committed
feat(parser): implement RawMessage
The NewRawMessage function returns a RawMessage struct, which has broken the given commit message down into separate lines, and also grouped the lines into paragraphs. This should make it easier to implement proper conventional commit parser, linter, and formatter.
1 parent 99a28a0 commit ce4b06f

File tree

8 files changed

+1771
-0
lines changed

8 files changed

+1771
-0
lines changed

go.mod

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module github.com/romdo/go-conventionalcommit
2+
3+
go 1.15
4+
5+
require github.com/stretchr/testify v1.7.0

go.sum

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
2+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
4+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
5+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
6+
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
7+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
8+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
9+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
10+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
11+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

line.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package conventionalcommit
2+
3+
const (
4+
lf = 10 // linefeed ("\n") character
5+
cr = 13 // carriage return ("\r") character
6+
)
7+
8+
// Line represents a single line of text defined as; A continuous sequence of
9+
// bytes which do not contain a \r (carriage return) or \n (line-feed) byte.
10+
type Line struct {
11+
// Line number within commit message, starting a 1 rather than 0, as
12+
// text viewed in a text editor starts on line 1, not line 0.
13+
Number int
14+
15+
// Content is the raw bytes that make up the text content in the line.
16+
Content []byte
17+
18+
// Break is the linebreak type used at the end of the line. It will be one
19+
// of "\n", "\r\n", "\r", or empty if it is the very last line.
20+
Break []byte
21+
}
22+
23+
// Lines is a slice of *Line types with some helper methods attached.
24+
type Lines []*Line
25+
26+
// NewLines breaks the given byte slice down into a slice of Line structs,
27+
// allowing easier inspection and manipulation of content on a line-by-line
28+
// basis.
29+
func NewLines(content []byte) Lines {
30+
r := Lines{}
31+
32+
if len(content) == 0 {
33+
return r
34+
}
35+
36+
// List of start/end offsets for each line break.
37+
var breaks [][]int
38+
39+
// Locate each line break within content.
40+
for i := 0; i < len(content); i++ {
41+
if content[i] == lf {
42+
breaks = append(breaks, []int{i, i + 1})
43+
} else if content[i] == cr {
44+
b := []int{i, i + 1}
45+
if i+1 < len(content) && content[i+1] == lf {
46+
b[1]++
47+
i++
48+
}
49+
breaks = append(breaks, b)
50+
}
51+
}
52+
53+
// Return a single line if there are no line breaks.
54+
if len(breaks) == 0 {
55+
return Lines{{Number: 1, Content: content, Break: []byte{}}}
56+
}
57+
58+
// Extract each line based on linebreak offsets.
59+
offset := 0
60+
for n, loc := range breaks {
61+
r = append(r, &Line{
62+
Number: n + 1,
63+
Content: content[offset:loc[0]],
64+
Break: content[loc[0]:loc[1]],
65+
})
66+
offset = loc[1]
67+
}
68+
69+
// Extract final line
70+
r = append(r, &Line{
71+
Number: len(breaks) + 1,
72+
Content: content[offset:],
73+
Break: []byte{},
74+
})
75+
76+
return r
77+
}
78+
79+
// Bytes combines all Lines into a single byte slice, retaining the original
80+
// line break types for each line.
81+
func (s Lines) Bytes() []byte {
82+
// Pre-calculate capacity of result byte slice.
83+
size := 0
84+
for _, l := range s {
85+
size = size + len(l.Content) + len(l.Break)
86+
}
87+
88+
b := make([]byte, 0, size)
89+
90+
for _, l := range s {
91+
b = append(b, l.Content...)
92+
b = append(b, l.Break...)
93+
}
94+
95+
return b
96+
}
97+
98+
// Bytes combines all Lines into a single string, retaining the original line
99+
// break types for each line.
100+
func (s Lines) String() string {
101+
return string(s.Bytes())
102+
}

0 commit comments

Comments
 (0)