Skip to content

Commit 0d5c826

Browse files
authored
Use bufio.Scanner to find line numbers in templates #5
2 parents c5ad941 + 71f6ca4 commit 0d5c826

File tree

7 files changed

+101
-25
lines changed

7 files changed

+101
-25
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,4 @@ _testmain.go
3131
*.exe
3232
*.test
3333
*.prof
34+
/xspreak

testdata/tmpl/five.tmpl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<article>
2+
<h2 class="font-display text-3xl tracking-tight text-slate-900 dark:text-white">Privacy</h2>
3+
<div class="prose prose-slate max-w-none dark:prose-invert dark:text-slate-400 prose-headings:scroll-mt-28 prose-headings:font-display prose-headings:font-normal lg:prose-headings:scroll-mt-[8.5rem] prose-lead:text-slate-500 dark:prose-lead:text-slate-400 prose-a:font-semibold dark:prose-a:text-sky-400 prose-a:no-underline prose-a:shadow-[inset_0_-2px_0_0_var(--tw-prose-background,#fff),inset_0_calc(-1*(var(--tw-prose-underline-size,4px)+2px))_0_0_var(--tw-prose-underline,theme(colors.sky.300))] hover:prose-a:[--tw-prose-underline-size:6px] dark:[--tw-prose-background:theme(colors.slate.900)] dark:prose-a:shadow-[inset_0_calc(-1*var(--tw-prose-underline-size,2px))_0_0_var(--tw-prose-underline,theme(colors.sky.800))] dark:hover:prose-a:[--tw-prose-underline-size:6px] prose-pre:rounded-xl prose-pre:bg-slate-900 prose-pre:shadow-lg dark:prose-pre:bg-slate-800/60 dark:prose-pre:shadow-none dark:prose-pre:ring-1 dark:prose-pre:ring-slate-300/10 dark:prose-hr:border-slate-800">
4+
<p>Emails you share with this site will be used to
5+
provide you with the data about them you've requested. That data, including the entire content of the email, is made available without any authentication needed at a semi-private URL.</p>
6+
<p>We don't currently plan on using any of that data for anything other than ensuring smooth running of the site.</p>
7+
<p><a x-link href="contact">Contact us</a> if you have any questions.</p>
8+
</div>
9+
</article>

testdata/tmpl/seven.parens

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
3+
{{ printf "%s" ( .X "foo" ) }}
4+
{{ .X "bar" }}

tmpl/parse.go

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
package tmpl
22

33
import (
4+
"bufio"
45
"bytes"
56
"go/token"
67
"io"
78
"os"
89
"strings"
9-
"text/scanner"
1010
"text/template/parse"
1111
)
1212

@@ -71,24 +71,14 @@ func extractLineInfos(filename string, src io.Reader) []token.Position {
7171
infos := make([]token.Position, 0, 50)
7272
infos = append(infos, token.Position{Filename: filename, Offset: 0, Line: 1, Column: 1})
7373

74-
var s scanner.Scanner
75-
s.Filename = filename
76-
s.Init(src)
77-
s.Whitespace ^= 1<<'\t' | 1<<'\n'
78-
s.Mode ^= scanner.SkipComments
79-
80-
for tok := s.Scan(); tok != scanner.EOF; tok = s.Scan() {
81-
switch tok {
82-
case '\n':
83-
p := s.Pos()
84-
tokPos := token.Position{
85-
Filename: p.Filename,
86-
Offset: p.Offset,
87-
Line: p.Line,
88-
Column: p.Column,
89-
}
90-
infos = append(infos, tokPos)
91-
}
74+
scanner := bufio.NewScanner(src)
75+
offset := 0
76+
line := 1
77+
for scanner.Scan() {
78+
offset += len(scanner.Text())
79+
infos = append(infos, token.Position{Filename: filename, Offset: offset, Line: line, Column: len(scanner.Text())})
80+
offset++
81+
line++
9282
}
9383

9484
return infos

tmpl/parse_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package tmpl
22

33
import (
4+
"os"
45
"testing"
56

67
"github.com/stretchr/testify/assert"
@@ -37,3 +38,14 @@ multiline */}}
3738
res.ExtractComments()
3839
assert.Len(t, res.Comments, 3)
3940
}
41+
42+
func TestParseHtml(t *testing.T) {
43+
text, err := os.ReadFile("../testdata/tmpl/five.tmpl")
44+
if err != nil {
45+
panic(err)
46+
}
47+
48+
res, err := ParseBytes("test", text)
49+
assert.NoError(t, err)
50+
require.NotNil(t, res)
51+
}

tmplextractors/command.go

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,11 @@ func (c *commandExtractor) Run(_ context.Context, extractCtx *extractors.Context
4343
}
4444

4545
for _, cmd := range pipe.Cmds {
46-
iss := extractIssue(cmd, extractCtx, template)
47-
if iss == nil {
48-
continue
46+
isses := extractIssues(cmd, extractCtx, template)
47+
for _, iss := range isses {
48+
iss.Flags = append(iss.Flags, "go-template")
49+
issues = append(issues, iss)
4950
}
50-
51-
iss.Flags = append(iss.Flags, "go-template")
52-
issues = append(issues, *iss)
5351
}
5452

5553
return
@@ -62,6 +60,29 @@ func (c *commandExtractor) Name() string {
6260
return "tmpl_command"
6361
}
6462

63+
func walkNode(n parse.Node, extractCtx *extractors.Context, template *tmpl.Template, results *[]result.Issue) {
64+
switch v := n.(type) {
65+
case *parse.CommandNode:
66+
iss := extractIssue(v, extractCtx, template)
67+
if iss != nil {
68+
*results = append(*results, *iss)
69+
}
70+
for _, node := range v.Args {
71+
walkNode(node, extractCtx, template, results)
72+
}
73+
case *parse.PipeNode:
74+
for _, node := range v.Cmds {
75+
walkNode(node, extractCtx, template, results)
76+
}
77+
}
78+
}
79+
80+
func extractIssues(cmd *parse.CommandNode, extractCtx *extractors.Context, template *tmpl.Template) []result.Issue {
81+
ret := []result.Issue{}
82+
walkNode(cmd, extractCtx, template, &ret)
83+
return ret
84+
}
85+
6586
func extractIssue(cmd *parse.CommandNode, extractCtx *extractors.Context, template *tmpl.Template) *result.Issue {
6687
if cmd == nil {
6788
return nil

tmplextractors/command_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,3 +146,42 @@ func TestComplex(t *testing.T) {
146146
got := collectIssueStrings(issues)
147147
assert.ElementsMatch(t, want, got)
148148
}
149+
150+
func TestParenthesised(t *testing.T) {
151+
cfg := config.NewDefault()
152+
cfg.SourceDir = testdataDir
153+
cfg.ExtractErrors = false
154+
cfg.Keywords = []*tmpl.Keyword{
155+
{
156+
Name: ".X",
157+
SingularPos: 0,
158+
PluralPos: -1,
159+
ContextPos: -1,
160+
DomainPos: -1,
161+
},
162+
}
163+
cfg.TemplatePatterns = []string{
164+
testdataTemplates + "/**/seven.parens",
165+
}
166+
167+
require.NoError(t, cfg.Prepare())
168+
169+
ctx := context.Background()
170+
contextLoader := extract.NewContextLoader(cfg)
171+
172+
extractCtx, err := contextLoader.Load(ctx)
173+
require.NoError(t, err)
174+
175+
runner, err := extract.NewRunner(cfg, extractCtx.Packages)
176+
require.NoError(t, err)
177+
178+
issues, err := runner.Run(ctx, extractCtx, []extractors.Extractor{NewCommandExtractor()})
179+
require.NoError(t, err)
180+
181+
want := []string{
182+
"foo",
183+
"bar",
184+
}
185+
got := collectIssueStrings(issues)
186+
assert.ElementsMatch(t, want, got)
187+
}

0 commit comments

Comments
 (0)