Skip to content

Commit c72bb94

Browse files
committed
feat(parser): handle incomplete scopes
1 parent 73ddab7 commit c72bb94

File tree

3 files changed

+31
-12
lines changed

3 files changed

+31
-12
lines changed

pkg/parser/combinators.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
// of a pointer wouldn't save space.
1414

1515
type Result struct {
16+
// The Results of each off a the child parsers
1617
Children []Result
1718
Type string
1819
Value string
@@ -81,6 +82,7 @@ func Marked(mark string) func(Parser) Parser {
8182
}
8283
}
8384

85+
// Note that `Opt` never returns an error.
8486
func Opt(parser Parser) Parser {
8587
return func(input []rune) (*Result, error) {
8688
result, err := parser(input)
@@ -185,6 +187,7 @@ func Some(parsers ...Parser) Parser {
185187
}
186188
}
187189

190+
// Matches the end of the input
188191
func Empty(input []rune) (*Result, error) {
189192
if len(input) == 0 {
190193
return nil, nil

pkg/parser/parser.go

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func (cc *CC) ToString() string {
7979
var Newline = Marked("Newline")(Any(LiteralRune('\n'), Tag("\r\n")))
8080

8181
var DoubleNewline = Sequence(Newline, Newline)
82-
var ColonSep = Tag(": ")
82+
var ColonSep = Regex(": ?") // accept a colon with or without a space after it
8383

8484
// The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.
8585

@@ -90,14 +90,15 @@ var ColonSep = Tag(": ")
9090
// A description MUST immediately follow the colon and space after the type/scope prefix. The description is a short summary of the code changes, e.g., fix: array parsing issue when multiple spaces were contained in string.
9191

9292
var CommitType Parser = Marked("CommitType")(
93-
TakeUntil(Any(BreakingChangeBang, Tag(":"), Tag("("), Empty)),
93+
TakeUntil(Any(BreakingChangeBang, Tag(":"), Tag("("), Newline, Empty)),
9494
)
9595

9696
// A scope MAY be provided after a type. A scope MUST consist of a noun describing a section of the codebase surrounded by parenthesis, e.g., fix(parser):
9797
var Scope Parser = Marked("Scope")(Delimited(Tag("("), TakeUntil(Tag(")")), Tag(")")))
9898
var BreakingChangeBang Parser = Marked("BreakingChangeBang")(Tag("!"))
9999
var ShortDescription Parser = Marked("Description")(TakeUntil(Any(Empty, Newline)))
100100

101+
// The bit before the description, e.g. "feat", "fix(scope)", "refactor!", etc.
101102
var Context = Sequence(CommitType, Opt(Scope), Opt(BreakingChangeBang))
102103

103104
var BreakingChange = Any(Tag("BREAKING CHANGE"), Tag("BREAKING-CHANGE"))
@@ -114,18 +115,31 @@ var Footer = Marked("Footer")(
114115
)
115116
var Footers = Marked("Footers")(Many0(Footer))
116117

118+
var asMuchOfScopeAsPossible = Marked("Scope")(
119+
Delimited(
120+
Tag("("),
121+
TakeUntil(Any(Tag(")"), Empty, Newline, Tag(":"), Tag("!"))),
122+
Opt(Tag(")")),
123+
),
124+
)
125+
126+
var asMuchOfCCAsPossible = Some(
127+
CommitType, Opt(asMuchOfScopeAsPossible), Opt(BreakingChangeBang), ColonSep, ShortDescription,
128+
Opt(Newline), Opt(Newline),
129+
Opt(Body),
130+
Opt(Footers),
131+
)
132+
117133
func ParseAsMuchOfCCAsPossible(fullCommit string) (*CC, error) {
118-
parsed, err := Some(
119-
CommitType, Opt(Scope), Opt(BreakingChangeBang), ColonSep, ShortDescription,
120-
Opt(Newline), Opt(Newline),
121-
Opt(Body),
122-
Opt(Footers),
123-
)([]rune(fullCommit))
134+
parsed, err := asMuchOfCCAsPossible([]rune(fullCommit))
124135
result := &CC{}
125136
if parsed != nil && parsed.Children != nil {
126137
for _, token := range parsed.Children {
127138
result = result.Ingest(token)
128139
}
129140
}
141+
if parsed.Remaining != nil {
142+
result.Body += string(parsed.Remaining)
143+
}
130144
return result, err
131145
}

pkg/parser/parser_test.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ func TestParsingFullCommit(t *testing.T) {
148148
t.FailNow()
149149
}
150150
if actual.Scope != expected.Scope {
151-
fmt.Printf("Scope: expected: %+v actual: %+v\n", expected.Type, actual.Type)
151+
fmt.Printf("Scope: expected: %+v actual: %+v\n", expected.Scope, actual.Scope)
152152
t.FailNow()
153153
}
154154
if actual.BreakingChange != expected.BreakingChange {
@@ -278,7 +278,9 @@ func TestParsingPartialCommit(t *testing.T) {
278278
}
279279
}
280280
}
281-
t.Run("", test("feat", CC{Type: "feat"}))
282-
t.Run("", test("feat:", CC{Type: "feat"}))
283-
t.Run("", test("feat: ", CC{Type: "feat"}))
281+
t.Run("bare `type`", test("feat", CC{Type: "feat"}))
282+
t.Run("valid `type:`", test("feat:", CC{Type: "feat"}))
283+
t.Run("valid `type: `", test("feat: ", CC{Type: "feat"}))
284+
285+
t.Run("invalid `type\nbody`", test("feat\nbody", CC{Type: "feat", Body: "\nbody"}))
284286
}

0 commit comments

Comments
 (0)