123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- // 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) {}
|