Skip to content
This repository has been archived by the owner on Jan 5, 2019. It is now read-only.

Commit

Permalink
LineNumbers
Browse files Browse the repository at this point in the history
  • Loading branch information
robbertvanginkel authored and Ernest Micklei committed Nov 24, 2017
1 parent 23424ce commit c8e235e
Show file tree
Hide file tree
Showing 28 changed files with 264 additions and 143 deletions.
15 changes: 7 additions & 8 deletions comment.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ import "strings"

// Comment holds a message.
type Comment struct {
lineNumber int
LineNumber int
Lines []string
Cstyle bool // refers to /* ... */, C++ style is using //
ExtraSlash bool
}

// newComment returns a comment.
func newComment(lit string) *Comment {
func newComment(line int, lit string) *Comment {
nonEmpty := []string{}
extraSlash := false
lines := strings.Split(lit, "\n")
Expand All @@ -51,7 +51,7 @@ func newComment(lit string) *Comment {
nonEmpty = append(nonEmpty, lit)
}
}
return &Comment{Lines: nonEmpty, Cstyle: len(lines) > 1, ExtraSlash: extraSlash}
return &Comment{LineNumber: line, Lines: nonEmpty, Cstyle: len(lines) > 1, ExtraSlash: extraSlash}
}

// columns is part of columnsPrintable
Expand Down Expand Up @@ -114,15 +114,15 @@ type commentInliner interface {
func maybeScanInlineComment(p *Parser, c elementContainer) {
currentLine := p.s.line
// see if there is an inline Comment
tok, lit := p.scanIgnoreWhitespace()
line, tok, lit := p.scanIgnoreWhitespace()
esize := len(c.elements())
// seen comment and on same line and elements have been added
if tCOMMENT == tok && p.s.line <= currentLine+1 && esize > 0 {
// if the last added element can have an inline comment then set it
last := c.elements()[esize-1]
if inliner, ok := last.(commentInliner); ok {
// TODO skip multiline?
inliner.inlineComment(newComment(lit))
inliner.inlineComment(newComment(line, lit))
}
} else {
p.unscan()
Expand All @@ -142,13 +142,12 @@ func takeLastComment(list []Visitee) (*Comment, []Visitee) {

// mergeOrReturnComment creates a new comment and tries to merge it with the last element (if is a comment and is on the next line).
func mergeOrReturnComment(elements []Visitee, lit string, lineNumber int) *Comment {
com := newComment(lit)
com.lineNumber = lineNumber
com := newComment(lineNumber, lit)
// last element must be a comment to merge +
// do not merge c-style comments +
// last comment line was on previous line
if esize := len(elements); esize > 0 {
if last, ok := elements[esize-1].(*Comment); ok && !last.Cstyle && lineNumber <= last.lineNumber+len(last.Lines) { // less than because last line of file could be inline comment
if last, ok := elements[esize-1].(*Comment); ok && !last.Cstyle && lineNumber <= last.LineNumber+len(last.Lines) { // less than because last line of file could be inline comment
last.Merge(com)
// mark as merged
com = nil
Expand Down
14 changes: 10 additions & 4 deletions comment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ import (
)

func TestCreateComment(t *testing.T) {
c0 := newComment("")
c0 := newComment(0, "")
if got, want := len(c0.Lines), 1; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
c1 := newComment(`hello
c1 := newComment(0, `hello
world`)
if got, want := len(c1.Lines), 2; got != want {
t.Errorf("got [%v] want [%v]", got, want)
Expand All @@ -49,8 +49,8 @@ world`)
}

func TestTakeLastComment(t *testing.T) {
c0 := newComment("hi")
c1 := newComment("there")
c0 := newComment(0, "hi")
c1 := newComment(0, "there")
_, l := takeLastComment([]Visitee{c0, c1})
if got, want := len(l), 1; got != want {
t.Fatalf("got [%v] want [%v]", got, want)
Expand Down Expand Up @@ -83,6 +83,9 @@ func TestParseCommentWithEmptyLinesAndTripleSlash(t *testing.T) {
if got, want := def.Elements[0].(*Comment).Lines[4], " comment 4"; got != want {
t.Fatalf("got [%v] want [%v]", got, want)
}
if got, want := def.Elements[0].(*Comment).LineNumber, 2; got != want {
t.Fatalf("got [%d] want [%d]", got, want)
}
}

func TestParseCommentWithTripleSlash(t *testing.T) {
Expand All @@ -104,4 +107,7 @@ func TestParseCommentWithTripleSlash(t *testing.T) {
if got, want := def.Elements[0].(*Comment).Lines[0], " comment 1"; got != want {
t.Fatalf("got [%v] want [%v]", got, want)
}
if got, want := def.Elements[0].(*Comment).LineNumber, 2; got != want {
t.Fatalf("got [%d] want [%d]", got, want)
}
}
27 changes: 16 additions & 11 deletions enum.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@ import "strconv"

// Enum definition consists of a name and an enum body.
type Enum struct {
Comment *Comment
Name string
Elements []Visitee
LineNumber int
Comment *Comment
Name string
Elements []Visitee
}

// Accept dispatches the call to the visitor.
Expand Down Expand Up @@ -60,26 +61,27 @@ func (e *Enum) takeLastComment() (last *Comment) {
}

func (e *Enum) parse(p *Parser) error {
tok, lit := p.scanIgnoreWhitespace()
line, tok, lit := p.scanIgnoreWhitespace()
if tok != tIDENT {
if !isKeyword(tok) {
return p.unexpected(lit, "enum identifier", e)
}
}
e.Name = lit
tok, lit = p.scanIgnoreWhitespace()
_, tok, lit = p.scanIgnoreWhitespace()
if tok != tLEFTCURLY {
return p.unexpected(lit, "enum opening {", e)
}
for {
tok, lit = p.scanIgnoreWhitespace()
line, tok, lit = p.scanIgnoreWhitespace()
switch tok {
case tCOMMENT:
if com := mergeOrReturnComment(e.elements(), lit, p.s.line); com != nil { // not merged?
if com := mergeOrReturnComment(e.elements(), lit, line); com != nil { // not merged?
e.Elements = append(e.Elements, com)
}
case tOPTION:
v := new(Option)
v.LineNumber = line
v.Comment = e.takeLastComment()
err := v.parse(p)
if err != nil {
Expand All @@ -93,6 +95,7 @@ func (e *Enum) parse(p *Parser) error {
default:
p.unscan()
f := new(EnumField)
f.LineNumber = line
f.Comment = e.takeLastComment()
err := f.parse(p)
if err != nil {
Expand All @@ -110,6 +113,7 @@ done:

// EnumField is part of the body of an Enum.
type EnumField struct {
LineNumber int
Comment *Comment
Name string
Integer int
Expand Down Expand Up @@ -146,14 +150,14 @@ func (f EnumField) columns() (cols []aligned) {
}

func (f *EnumField) parse(p *Parser) error {
tok, lit := p.scanIgnoreWhitespace()
line, tok, lit := p.scanIgnoreWhitespace()
if tok != tIDENT {
if !isKeyword(tok) {
return p.unexpected(lit, "enum field identifier", f)
}
}
f.Name = lit
tok, lit = p.scanIgnoreWhitespace()
line, tok, lit = p.scanIgnoreWhitespace()
if tok != tEQUALS {
return p.unexpected(lit, "enum field =", f)
}
Expand All @@ -162,16 +166,17 @@ func (f *EnumField) parse(p *Parser) error {
return p.unexpected(lit, "enum field integer", f)
}
f.Integer = i
tok, lit = p.scanIgnoreWhitespace()
line, tok, lit = p.scanIgnoreWhitespace()
if tok == tLEFTSQUARE {
o := new(Option)
o.LineNumber = line
o.IsEmbedded = true
err := o.parse(p)
if err != nil {
return err
}
f.ValueOption = o
tok, lit = p.scanIgnoreWhitespace()
line, tok, lit = p.scanIgnoreWhitespace()
if tok != tRIGHTSQUARE {
return p.unexpected(lit, "option closing ]", f)
}
Expand Down
11 changes: 10 additions & 1 deletion enum_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ package proto
import "testing"

func TestEnum(t *testing.T) {
proto := `
proto := `
// enum
enum EnumAllowingAlias {
option allow_alias = true;
Expand All @@ -52,10 +52,16 @@ enum EnumAllowingAlias {
if got, want := enums[0].Comment.Message(), " enum"; got != want {
t.Errorf("got [%v] want [%v]", enums[0].Comment, want)
}
if got, want := enums[0].LineNumber, 3; got != want {
t.Errorf("got [%d] want [%d]", got, want)
}
ef1 := enums[0].Elements[1].(*EnumField)
if got, want := ef1.Integer, 0; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
if got, want := ef1.LineNumber, 5; got != want {
t.Errorf("got [%d] want [%d]", got, want)
}
ef3 := enums[0].Elements[3].(*EnumField)
if got, want := ef3.Integer, 2; got != want {
t.Errorf("got [%v] want [%v]", got, want)
Expand All @@ -66,4 +72,7 @@ enum EnumAllowingAlias {
if got, want := ef3.ValueOption.Constant.Source, "hello world"; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
if got, want := ef3.LineNumber, 7; got != want {
t.Errorf("got [%d] want [%d]", got, want)
}
}
1 change: 1 addition & 0 deletions extensions.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ package proto
// Extensions declare that a range of field numbers in a message are available for third-party extensions.
// proto2 only
type Extensions struct {
LineNumber int
Comment *Comment
Ranges []Range
InlineComment *Comment
Expand Down
5 changes: 4 additions & 1 deletion extensions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ package proto
import "testing"

func TestExtensions(t *testing.T) {
proto := `message M {
proto := `message M {
// extensions
extensions 4, 20 to max; // max
}`
Expand All @@ -44,6 +44,9 @@ func TestExtensions(t *testing.T) {
if got, want := len(f.Ranges), 2; got != want {
t.Fatalf("got [%d] want [%d]", got, want)
}
if got, want := f.LineNumber, 3; got != want {
t.Fatalf("got [%d] want [%d]", got, want)
}
if got, want := f.Ranges[1].String(), "20 to max"; got != want {
t.Errorf("got [%s] want [%s]", got, want)
}
Expand Down
23 changes: 13 additions & 10 deletions field.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import "strconv"

// Field is an abstract message field.
type Field struct {
LineNumber int
Comment *Comment
Name string
Type string
Expand Down Expand Up @@ -94,7 +95,8 @@ func (f *NormalField) columns() (cols []aligned) {
// [ "repeated" | "optional" ] type fieldName "=" fieldNumber [ "[" fieldOptions "]" ] ";"
func (f *NormalField) parse(p *Parser) error {
for {
tok, lit := p.scanIgnoreWhitespace()
line, tok, lit := p.scanIgnoreWhitespace()
f.LineNumber = line
switch tok {
case tREPEATED:
f.Repeated = true
Expand All @@ -116,14 +118,14 @@ done:
// parseFieldAfterType expects:
// fieldName "=" fieldNumber [ "[" fieldOptions "]" ] ";
func parseFieldAfterType(f *Field, p *Parser) error {
tok, lit := p.scanIgnoreWhitespace()
line, tok, lit := p.scanIgnoreWhitespace()
if tok != tIDENT {
if !isKeyword(tok) {
return p.unexpected(lit, "field identifier", f)
}
}
f.Name = lit
tok, lit = p.scanIgnoreWhitespace()
line, tok, lit = p.scanIgnoreWhitespace()
if tok != tEQUALS {
return p.unexpected(lit, "field =", f)
}
Expand All @@ -133,22 +135,23 @@ func parseFieldAfterType(f *Field, p *Parser) error {
}
f.Sequence = i
// see if there are options
tok, lit = p.scanIgnoreWhitespace()
line, tok, lit = p.scanIgnoreWhitespace()
if tLEFTSQUARE != tok {
p.unscan()
return nil
}
// consume options
for {
o := new(Option)
o.LineNumber = line
o.IsEmbedded = true
err := o.parse(p)
if err != nil {
return err
}
f.Options = append(f.Options, o)

tok, lit = p.scanIgnoreWhitespace()
line, tok, lit = p.scanIgnoreWhitespace()
if tRIGHTSQUARE == tok {
break
}
Expand Down Expand Up @@ -205,25 +208,25 @@ func (f *MapField) columns() (cols []aligned) {
// keyType = "int32" | "int64" | "uint32" | "uint64" | "sint32" | "sint64" |
// "fixed32" | "fixed64" | "sfixed32" | "sfixed64" | "bool" | "string"
func (f *MapField) parse(p *Parser) error {
tok, lit := p.scanIgnoreWhitespace()
_, tok, lit := p.scanIgnoreWhitespace()
if tLESS != tok {
return p.unexpected(lit, "map keyType <", f)
}
tok, lit = p.scanIgnoreWhitespace()
_, tok, lit = p.scanIgnoreWhitespace()
if tIDENT != tok {
return p.unexpected(lit, "map identifier", f)
}
f.KeyType = lit
tok, lit = p.scanIgnoreWhitespace()
_, tok, lit = p.scanIgnoreWhitespace()
if tCOMMA != tok {
return p.unexpected(lit, "map type separator ,", f)
}
tok, lit = p.scanIgnoreWhitespace()
_, tok, lit = p.scanIgnoreWhitespace()
if tIDENT != tok {
return p.unexpected(lit, "map valueType identifier", f)
}
f.Type = lit
tok, lit = p.scanIgnoreWhitespace()
_, tok, lit = p.scanIgnoreWhitespace()
if tGREATER != tok {
return p.unexpected(lit, "mak valueType >", f)
}
Expand Down
3 changes: 3 additions & 0 deletions field_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ func TestField(t *testing.T) {
if got, want := f.Options[2].Constant.Source, "happy"; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
if got, want := f.LineNumber, 1; got != want {
t.Errorf("got [%v] want [%v]", got, want)
}
}

func TestFieldSimple(t *testing.T) {
Expand Down
Loading

0 comments on commit c8e235e

Please sign in to comment.