bsc/vendor/github.com/robertkrimen/otto/ast/comments.go

279 lines
7.1 KiB
Go
Raw Normal View History

package ast
import (
"fmt"
"github.com/robertkrimen/otto/file"
)
// CommentPosition determines where the comment is in a given context
type CommentPosition int
const (
_ CommentPosition = iota
LEADING // Before the pertinent expression
TRAILING // After the pertinent expression
KEY // Before a key in an object
COLON // After a colon in a field declaration
FINAL // Final comments in a block, not belonging to a specific expression or the comment after a trailing , in an array or object literal
IF // After an if keyword
WHILE // After a while keyword
DO // After do keyword
FOR // After a for keyword
WITH // After a with keyword
TBD
)
// Comment contains the data of the comment
type Comment struct {
Begin file.Idx
Text string
Position CommentPosition
}
// NewComment creates a new comment
func NewComment(text string, idx file.Idx) *Comment {
comment := &Comment{
Begin: idx,
Text: text,
Position: TBD,
}
return comment
}
// String returns a stringified version of the position
func (cp CommentPosition) String() string {
switch cp {
case LEADING:
return "Leading"
case TRAILING:
return "Trailing"
case KEY:
return "Key"
case COLON:
return "Colon"
case FINAL:
return "Final"
case IF:
return "If"
case WHILE:
return "While"
case DO:
return "Do"
case FOR:
return "For"
case WITH:
return "With"
default:
return "???"
}
}
// String returns a stringified version of the comment
func (c Comment) String() string {
return fmt.Sprintf("Comment: %v", c.Text)
}
// Comments defines the current view of comments from the parser
type Comments struct {
// CommentMap is a reference to the parser comment map
CommentMap CommentMap
// Comments lists the comments scanned, not linked to a node yet
Comments []*Comment
// future lists the comments after a line break during a sequence of comments
future []*Comment
// Current is node for which comments are linked to
Current Expression
// wasLineBreak determines if a line break occured while scanning for comments
wasLineBreak bool
// primary determines whether or not processing a primary expression
primary bool
// afterBlock determines whether or not being after a block statement
afterBlock bool
}
func NewComments() *Comments {
comments := &Comments{
CommentMap: CommentMap{},
}
return comments
}
func (c *Comments) String() string {
return fmt.Sprintf("NODE: %v, Comments: %v, Future: %v(LINEBREAK:%v)", c.Current, len(c.Comments), len(c.future), c.wasLineBreak)
}
// FetchAll returns all the currently scanned comments,
// including those from the next line
func (c *Comments) FetchAll() []*Comment {
defer func() {
c.Comments = nil
c.future = nil
}()
return append(c.Comments, c.future...)
}
// Fetch returns all the currently scanned comments
func (c *Comments) Fetch() []*Comment {
defer func() {
c.Comments = nil
}()
return c.Comments
}
// ResetLineBreak marks the beginning of a new statement
func (c *Comments) ResetLineBreak() {
c.wasLineBreak = false
}
// MarkPrimary will mark the context as processing a primary expression
func (c *Comments) MarkPrimary() {
c.primary = true
c.wasLineBreak = false
}
// AfterBlock will mark the context as being after a block.
func (c *Comments) AfterBlock() {
c.afterBlock = true
}
// AddComment adds a comment to the view.
// Depending on the context, comments are added normally or as post line break.
func (c *Comments) AddComment(comment *Comment) {
if c.primary {
if !c.wasLineBreak {
c.Comments = append(c.Comments, comment)
} else {
c.future = append(c.future, comment)
}
} else {
if !c.wasLineBreak || (c.Current == nil && !c.afterBlock) {
c.Comments = append(c.Comments, comment)
} else {
c.future = append(c.future, comment)
}
}
}
// MarkComments will mark the found comments as the given position.
func (c *Comments) MarkComments(position CommentPosition) {
for _, comment := range c.Comments {
if comment.Position == TBD {
comment.Position = position
}
}
for _, c := range c.future {
if c.Position == TBD {
c.Position = position
}
}
}
// Unset the current node and apply the comments to the current expression.
// Resets context variables.
func (c *Comments) Unset() {
if c.Current != nil {
c.applyComments(c.Current, c.Current, TRAILING)
c.Current = nil
}
c.wasLineBreak = false
c.primary = false
c.afterBlock = false
}
// SetExpression sets the current expression.
// It is applied the found comments, unless the previous expression has not been unset.
// It is skipped if the node is already set or if it is a part of the previous node.
func (c *Comments) SetExpression(node Expression) {
// Skipping same node
if c.Current == node {
return
}
if c.Current != nil && c.Current.Idx1() == node.Idx1() {
c.Current = node
return
}
previous := c.Current
c.Current = node
// Apply the found comments and futures to the node and the previous.
c.applyComments(node, previous, TRAILING)
}
// PostProcessNode applies all found comments to the given node
func (c *Comments) PostProcessNode(node Node) {
c.applyComments(node, nil, TRAILING)
}
// applyComments applies both the comments and the future comments to the given node and the previous one,
// based on the context.
func (c *Comments) applyComments(node, previous Node, position CommentPosition) {
if previous != nil {
c.CommentMap.AddComments(previous, c.Comments, position)
c.Comments = nil
} else {
c.CommentMap.AddComments(node, c.Comments, position)
c.Comments = nil
}
// Only apply the future comments to the node if the previous is set.
// This is for detecting end of line comments and which node comments on the following lines belongs to
if previous != nil {
c.CommentMap.AddComments(node, c.future, position)
c.future = nil
}
}
// AtLineBreak will mark a line break
func (c *Comments) AtLineBreak() {
c.wasLineBreak = true
}
// CommentMap is the data structure where all found comments are stored
type CommentMap map[Node][]*Comment
// AddComment adds a single comment to the map
func (cm CommentMap) AddComment(node Node, comment *Comment) {
list := cm[node]
list = append(list, comment)
cm[node] = list
}
// AddComments adds a slice of comments, given a node and an updated position
func (cm CommentMap) AddComments(node Node, comments []*Comment, position CommentPosition) {
for _, comment := range comments {
if comment.Position == TBD {
comment.Position = position
}
cm.AddComment(node, comment)
}
}
// Size returns the size of the map
func (cm CommentMap) Size() int {
size := 0
for _, comments := range cm {
size += len(comments)
}
return size
}
// MoveComments moves comments with a given position from a node to another
func (cm CommentMap) MoveComments(from, to Node, position CommentPosition) {
for i, c := range cm[from] {
if c.Position == position {
cm.AddComment(to, c)
// Remove the comment from the "from" slice
cm[from][i] = cm[from][len(cm[from])-1]
cm[from][len(cm[from])-1] = nil
cm[from] = cm[from][:len(cm[from])-1]
}
}
}