Skip to content

Commit eec2566

Browse files
authored
Add support to line numbers (#7)
* Buffer the original line * Add lines
1 parent f42c408 commit eec2566

2 files changed

Lines changed: 33 additions & 21 deletions

File tree

decoder.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
type Decoder struct {
1111
scanner *bufio.Scanner
1212
line string
13+
lineNo int
1314
done bool
1415
}
1516

@@ -18,6 +19,7 @@ func NewDecoder(r io.Reader) *Decoder {
1819
return &Decoder{
1920
scanner: bufio.NewScanner(r),
2021
line: "",
22+
lineNo: 0,
2123
done: false,
2224
}
2325
}
@@ -28,9 +30,11 @@ func (d *Decoder) peek() {
2830
d.done = true
2931
return
3032
}
31-
line := sanitiseLine(d.scanner.Text())
32-
d.line = line
33-
if len(d.line) == 0 && !d.done {
33+
34+
d.line = d.scanner.Text()
35+
line := sanitiseLine(d.line)
36+
d.lineNo++
37+
if len(line) == 0 && !d.done {
3438
d.peek()
3539
}
3640
}
@@ -54,8 +58,9 @@ func (d *Decoder) More() bool {
5458
// Token parses the next available line in the CODEOWNERS file.
5559
// If More was never called it will return an empty token.
5660
// After end of file Token will always return the last line.
57-
func (d *Decoder) Token() Token {
58-
line := strings.ReplaceAll(d.line, "\\ ", "\\s")
61+
func (d *Decoder) Token() (Token, int) {
62+
line := sanitiseLine(d.line)
63+
line = strings.ReplaceAll(line, "\\ ", "\\s")
5964

6065
data := strings.Split(line, " ")
6166

@@ -66,7 +71,7 @@ func (d *Decoder) Token() Token {
6671
return Token{
6772
path: data[0],
6873
owners: data[1:],
69-
}
74+
}, d.lineNo
7075
}
7176

7277
// Token providers reading capabilities for every CODEOWNERS line

decoder_test.go

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package codeowners_test
33
import (
44
"fmt"
55
"reflect"
6+
"strconv"
67
"strings"
78
"testing"
89

@@ -15,8 +16,8 @@ func exec(input string) ([][]string, int) {
1516
c := 0
1617
for decoder.More() {
1718
c++
18-
token := decoder.Token()
19-
got = append(got, append([]string{token.Path()}, token.Owners()...))
19+
token, line := decoder.Token()
20+
got = append(got, append([]string{strconv.Itoa(line), token.Path()}, token.Owners()...))
2021
}
2122
return got, c
2223
}
@@ -33,27 +34,27 @@ func assert(t *testing.T, input string, want [][]string) {
3334

3435
func TestSimple(t *testing.T) {
3536
assert(t, `* test@example.org`, [][]string{
36-
{"*", "test@example.org"},
37+
{"1", "*", "test@example.org"},
3738
})
3839
}
3940

4041
func TestMultipleOwners(t *testing.T) {
4142
assert(t, `* test@example.org @owner @company/team`, [][]string{
42-
{"*", "test@example.org", "@owner", "@company/team"},
43+
{"1", "*", "test@example.org", "@owner", "@company/team"},
4344
})
4445
}
4546

4647
func TestFilesWithSpaces(t *testing.T) {
4748
assert(t, `file\ with\ spaces @owner`, [][]string{
48-
{"file with spaces", "@owner"},
49+
{"1", "file with spaces", "@owner"},
4950
})
5051
}
5152

5253
func TestMultipleLines(t *testing.T) {
5354
assert(t, `* test@example.org
5455
file @owner`, [][]string{
55-
{"*", "test@example.org"},
56-
{"file", "@owner"},
56+
{"1", "*", "test@example.org"},
57+
{"2", "file", "@owner"},
5758
})
5859
}
5960

@@ -72,23 +73,23 @@ file @owner
7273
7374
7475
`, [][]string{
75-
{"*", "test@example.org"},
76-
{"file", "@owner"},
76+
{"1", "*", "test@example.org"},
77+
{"5", "file", "@owner"},
7778
})
7879
}
7980

8081
func TestIgnoreComments(t *testing.T) {
8182
assert(t, `* test@example.org # comment
8283
# comment
8384
file @owner`, [][]string{
84-
{"*", "test@example.org"},
85-
{"file", "@owner"},
85+
{"1", "*", "test@example.org"},
86+
{"3", "file", "@owner"},
8687
})
8788
}
8889

8990
func TestNoOwners(t *testing.T) {
9091
assert(t, `*`, [][]string{
91-
{"*"},
92+
{"1", "*"},
9293
})
9394
}
9495

@@ -98,7 +99,10 @@ func TestLastToken(t *testing.T) {
9899
t.Error("More should be true")
99100
}
100101
for i := 0; i < 3; i++ { //calling 3 times to prove it always returns the last line
101-
token := decoder.Token()
102+
token, line := decoder.Token()
103+
if line != 1 {
104+
t.Error("Line should be '1'")
105+
}
102106
if token.Path() != "filepattern" {
103107
t.Error("Path should be 'filepattern'")
104108
}
@@ -114,7 +118,7 @@ func TestLastToken(t *testing.T) {
114118

115119
func TestMoreNotCalled(t *testing.T) {
116120
decoder := codeowners.NewDecoder(strings.NewReader(`filepattern @owner`))
117-
token := decoder.Token()
121+
token, _ := decoder.Token()
118122
if token.Path() != "" {
119123
t.Error("Path should be empty")
120124
}
@@ -127,13 +131,16 @@ func ExampleDecoder() {
127131
decoder := codeowners.NewDecoder(strings.NewReader(`* test@example.org
128132
filepattern @owner`))
129133
for decoder.More() {
130-
token := decoder.Token()
134+
token, line := decoder.Token()
135+
fmt.Printf("Line: %d\n", line)
131136
fmt.Printf("File Pattern: %s\n", token.Path())
132137
fmt.Printf("Owners: %v\n", token.Owners())
133138
}
134139
// Output:
140+
// Line: 1
135141
// File Pattern: *
136142
// Owners: [test@example.org]
143+
// Line: 2
137144
// File Pattern: filepattern
138145
// Owners: [@owner]
139146
}

0 commit comments

Comments
 (0)