123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292 |
- package ast
- import (
- "fmt"
- "github.com/robertkrimen/otto/file"
- )
- // CommentPosition determines where the comment is in a given context.
- type CommentPosition int
- // Available comment positions.
- const (
- _ CommentPosition = iota
- // LEADING is before the pertinent expression.
- LEADING
- // TRAILING is after the pertinent expression.
- TRAILING
- // KEY is before a key in an object.
- KEY
- // COLON is after a colon in a field declaration.
- COLON
- // FINAL is the final comments in a block, not belonging to a specific expression or the comment after a trailing , in an array or object literal.
- FINAL
- // IF is after an if keyword.
- IF
- // WHILE is after a while keyword.
- WHILE
- // DO is after do keyword.
- DO
- // FOR is after a for keyword.
- FOR
- // WITH is after a with keyword.
- WITH
- // TBD is unknown.
- TBD
- )
- // Comment contains the data of the comment.
- type Comment struct {
- Text string
- Begin file.Idx
- 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
- // Current is node for which comments are linked to
- Current Expression
- // future lists the comments after a line break during a sequence of comments
- future []*Comment
- // wasLineBreak determines if a line break occurred 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
- }
- // NewComments returns a new Comments.
- 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]
- }
- }
- }
|