You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
146 lines
4.6 KiB
146 lines
4.6 KiB
// Copyright (c) 2017 Ernest Micklei |
|
// |
|
// MIT License |
|
// |
|
// Permission is hereby granted, free of charge, to any person obtaining |
|
// a copy of this software and associated documentation files (the |
|
// "Software"), to deal in the Software without restriction, including |
|
// without limitation the rights to use, copy, modify, merge, publish, |
|
// distribute, sublicense, and/or sell copies of the Software, and to |
|
// permit persons to whom the Software is furnished to do so, subject to |
|
// the following conditions: |
|
// |
|
// The above copyright notice and this permission notice shall be |
|
// included in all copies or substantial portions of the Software. |
|
// |
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|
|
|
package proto |
|
|
|
import ( |
|
"strings" |
|
"text/scanner" |
|
) |
|
|
|
// Comment one or more comment text lines, either in c- or c++ style. |
|
type Comment struct { |
|
Position scanner.Position |
|
// Lines are comment text lines without prefixes //, ///, /* or suffix */ |
|
Lines []string |
|
Cstyle bool // refers to /* ... */, C++ style is using // |
|
ExtraSlash bool // is true if the comment starts with 3 slashes |
|
} |
|
|
|
// newComment returns a comment. |
|
func newComment(pos scanner.Position, lit string) *Comment { |
|
extraSlash := strings.HasPrefix(lit, "///") |
|
isCstyle := strings.HasPrefix(lit, "/*") && strings.HasSuffix(lit, "*/") |
|
var lines []string |
|
if isCstyle { |
|
withoutMarkers := strings.TrimRight(strings.TrimLeft(lit, "/*"), "*/") |
|
lines = strings.Split(withoutMarkers, "\n") |
|
} else { |
|
lines = strings.Split(strings.TrimLeft(lit, "/"), "\n") |
|
} |
|
return &Comment{Position: pos, Lines: lines, Cstyle: isCstyle, ExtraSlash: extraSlash} |
|
} |
|
|
|
//type inlineComment struct { |
|
// line string |
|
// extraSlash bool |
|
//} |
|
|
|
// Accept dispatches the call to the visitor. |
|
func (c *Comment) Accept(v Visitor) { |
|
v.VisitComment(c) |
|
} |
|
|
|
// Merge appends all lines from the argument comment. |
|
func (c *Comment) Merge(other *Comment) { |
|
c.Lines = append(c.Lines, other.Lines...) |
|
c.Cstyle = c.Cstyle || other.Cstyle |
|
} |
|
|
|
func (c Comment) hasTextOnLine(line int) bool { |
|
if len(c.Lines) == 0 { |
|
return false |
|
} |
|
return c.Position.Line <= line && line <= c.Position.Line+len(c.Lines)-1 |
|
} |
|
|
|
// Message returns the first line or empty if no lines. |
|
func (c Comment) Message() string { |
|
if len(c.Lines) == 0 { |
|
return "" |
|
} |
|
return c.Lines[0] |
|
} |
|
|
|
// commentInliner is for types that can have an inline comment. |
|
type commentInliner interface { |
|
inlineComment(c *Comment) |
|
} |
|
|
|
// maybeScanInlineComment tries to scan comment on the current line ; if present then set it for the last element added. |
|
func maybeScanInlineComment(p *Parser, c elementContainer) { |
|
currentPos := p.scanner.Position |
|
// see if there is an inline Comment |
|
pos, tok, lit := p.next() |
|
esize := len(c.elements()) |
|
// seen comment and on same line and elements have been added |
|
if tCOMMENT == tok && pos.Line == currentPos.Line && 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(pos, lit)) |
|
} |
|
} else { |
|
p.nextPut(pos, tok, lit) |
|
} |
|
} |
|
|
|
// takeLastCommentIfEndsOnLine removes and returns the last element of the list if it is a Comment |
|
func takeLastCommentIfEndsOnLine(list []Visitee, line int) (*Comment, []Visitee) { |
|
if len(list) == 0 { |
|
return nil, list |
|
} |
|
if last, ok := list[len(list)-1].(*Comment); ok && last.hasTextOnLine(line) { |
|
return last, list[:len(list)-1] |
|
} |
|
return nil, list |
|
} |
|
|
|
// 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, pos scanner.Position) *Comment { |
|
com := newComment(pos, lit) |
|
esize := len(elements) |
|
if esize == 0 { |
|
return com |
|
} |
|
// last element must be a comment to merge |
|
last, ok := elements[esize-1].(*Comment) |
|
if !ok { |
|
return com |
|
} |
|
// do not merge c-style comments |
|
if last.Cstyle { |
|
return com |
|
} |
|
// last comment has text on previous line |
|
// TODO handle last line of file could be inline comment |
|
if !last.hasTextOnLine(pos.Line - 1) { |
|
return com |
|
} |
|
last.Merge(com) |
|
return nil |
|
} |
|
|
|
// parent is part of elementContainer |
|
func (c *Comment) parent(Visitee) {}
|
|
|