diff options
Diffstat (limited to 'vendor/github.com/robertkrimen/otto/ast/comments.go')
-rw-r--r-- | vendor/github.com/robertkrimen/otto/ast/comments.go | 278 |
1 files changed, 278 insertions, 0 deletions
diff --git a/vendor/github.com/robertkrimen/otto/ast/comments.go b/vendor/github.com/robertkrimen/otto/ast/comments.go new file mode 100644 index 000000000..ef2cc3d89 --- /dev/null +++ b/vendor/github.com/robertkrimen/otto/ast/comments.go @@ -0,0 +1,278 @@ +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] + } + } +} |