comment.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. // Copyright (c) 2017 Ernest Micklei
  2. //
  3. // MIT License
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining
  6. // a copy of this software and associated documentation files (the
  7. // "Software"), to deal in the Software without restriction, including
  8. // without limitation the rights to use, copy, modify, merge, publish,
  9. // distribute, sublicense, and/or sell copies of the Software, and to
  10. // permit persons to whom the Software is furnished to do so, subject to
  11. // the following conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be
  14. // included in all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  17. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  18. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  19. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  20. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  21. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  22. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  23. package proto
  24. import (
  25. "strings"
  26. "text/scanner"
  27. )
  28. // Comment one or more comment text lines, either in c- or c++ style.
  29. type Comment struct {
  30. Position scanner.Position
  31. // Lines are comment text lines without prefixes //, ///, /* or suffix */
  32. Lines []string
  33. Cstyle bool // refers to /* ... */, C++ style is using //
  34. ExtraSlash bool // is true if the comment starts with 3 slashes
  35. }
  36. // newComment returns a comment.
  37. func newComment(pos scanner.Position, lit string) *Comment {
  38. extraSlash := strings.HasPrefix(lit, "///")
  39. isCstyle := strings.HasPrefix(lit, "/*") && strings.HasSuffix(lit, "*/")
  40. var lines []string
  41. if isCstyle {
  42. withoutMarkers := strings.TrimRight(strings.TrimLeft(lit, "/*"), "*/")
  43. lines = strings.Split(withoutMarkers, "\n")
  44. } else {
  45. lines = strings.Split(strings.TrimLeft(lit, "/"), "\n")
  46. }
  47. return &Comment{Position: pos, Lines: lines, Cstyle: isCstyle, ExtraSlash: extraSlash}
  48. }
  49. //type inlineComment struct {
  50. // line string
  51. // extraSlash bool
  52. //}
  53. // Accept dispatches the call to the visitor.
  54. func (c *Comment) Accept(v Visitor) {
  55. v.VisitComment(c)
  56. }
  57. // Merge appends all lines from the argument comment.
  58. func (c *Comment) Merge(other *Comment) {
  59. c.Lines = append(c.Lines, other.Lines...)
  60. c.Cstyle = c.Cstyle || other.Cstyle
  61. }
  62. func (c Comment) hasTextOnLine(line int) bool {
  63. if len(c.Lines) == 0 {
  64. return false
  65. }
  66. return c.Position.Line <= line && line <= c.Position.Line+len(c.Lines)-1
  67. }
  68. // Message returns the first line or empty if no lines.
  69. func (c Comment) Message() string {
  70. if len(c.Lines) == 0 {
  71. return ""
  72. }
  73. return c.Lines[0]
  74. }
  75. // commentInliner is for types that can have an inline comment.
  76. type commentInliner interface {
  77. inlineComment(c *Comment)
  78. }
  79. // maybeScanInlineComment tries to scan comment on the current line ; if present then set it for the last element added.
  80. func maybeScanInlineComment(p *Parser, c elementContainer) {
  81. currentPos := p.scanner.Position
  82. // see if there is an inline Comment
  83. pos, tok, lit := p.next()
  84. esize := len(c.elements())
  85. // seen comment and on same line and elements have been added
  86. if tCOMMENT == tok && pos.Line == currentPos.Line && esize > 0 {
  87. // if the last added element can have an inline comment then set it
  88. last := c.elements()[esize-1]
  89. if inliner, ok := last.(commentInliner); ok {
  90. // TODO skip multiline?
  91. inliner.inlineComment(newComment(pos, lit))
  92. }
  93. } else {
  94. p.nextPut(pos, tok, lit)
  95. }
  96. }
  97. // takeLastCommentIfEndsOnLine removes and returns the last element of the list if it is a Comment
  98. func takeLastCommentIfEndsOnLine(list []Visitee, line int) (*Comment, []Visitee) {
  99. if len(list) == 0 {
  100. return nil, list
  101. }
  102. if last, ok := list[len(list)-1].(*Comment); ok && last.hasTextOnLine(line) {
  103. return last, list[:len(list)-1]
  104. }
  105. return nil, list
  106. }
  107. // mergeOrReturnComment creates a new comment and tries to merge it with the last element (if is a comment and is on the next line).
  108. func mergeOrReturnComment(elements []Visitee, lit string, pos scanner.Position) *Comment {
  109. com := newComment(pos, lit)
  110. esize := len(elements)
  111. if esize == 0 {
  112. return com
  113. }
  114. // last element must be a comment to merge
  115. last, ok := elements[esize-1].(*Comment)
  116. if !ok {
  117. return com
  118. }
  119. // do not merge c-style comments
  120. if last.Cstyle {
  121. return com
  122. }
  123. // last comment has text on previous line
  124. // TODO handle last line of file could be inline comment
  125. if !last.hasTextOnLine(pos.Line - 1) {
  126. return com
  127. }
  128. last.Merge(com)
  129. return nil
  130. }
  131. // parent is part of elementContainer
  132. func (c *Comment) parent(Visitee) {}