@@ -4,11 +4,13 @@ import (
4
4
"bytes"
5
5
"fmt"
6
6
"go/ast"
7
- "go/printer"
8
7
"go/token"
9
8
"reflect"
10
9
"sort"
11
10
"strings"
11
+
12
+ "github.com/dave/dst"
13
+ "github.com/dave/dst/decorator"
12
14
)
13
15
14
16
// Error reason strings.
@@ -194,20 +196,29 @@ type result struct {
194
196
// processor is the type that keeps track of the file and fileset and holds the
195
197
// results from parsing the AST.
196
198
type processor struct {
197
- config * Configuration
198
- file * ast.File
199
- fileSet * token.FileSet
200
- Result map [token.Pos ]result
201
- Warnings []string
199
+ config * Configuration
200
+ file * ast.File
201
+ fileSet * token.FileSet
202
+ decorator * decorator.Decorator
203
+ Result map [token.Pos ]result
204
+ Warnings []string
202
205
}
203
206
204
207
// newProcessorWithConfig will create a Processor with the passed configuration.
205
208
func newProcessorWithConfig (file * ast.File , fileSet * token.FileSet , cfg * Configuration ) * processor {
209
+ dec := decorator .NewDecorator (fileSet )
210
+
211
+ _ , err := dec .DecorateFile (file )
212
+ if err != nil {
213
+ panic (err )
214
+ }
215
+
206
216
return & processor {
207
- config : cfg ,
208
- file : file ,
209
- fileSet : fileSet ,
210
- Result : make (map [token.Pos ]result ),
217
+ config : cfg ,
218
+ file : file ,
219
+ fileSet : fileSet ,
220
+ decorator : dec ,
221
+ Result : make (map [token.Pos ]result ),
211
222
}
212
223
}
213
224
@@ -562,6 +573,28 @@ func (p *processor) parseBlockStatements(statements []ast.Stmt) {
562
573
// and look forward until we no longer have a cuddled decl.
563
574
var endNode ast.Node = previousDeclNode
564
575
576
+ // There's a difference on where `dst` decorates decl stmts based on
577
+ // if it's a single value spec or if it's a grouped var block or any
578
+ // kind of node with multiple child nodes.
579
+ // If we have a single `var a = 1` we need to move the decoration
580
+ // from the *dst.DeclStmt to the *dst.ValueSpec which holds the
581
+ // assigned value. This will make sure we keep the comment when
582
+ // combining all the specs.
583
+ moveCommentsIfGrouped := func (node * ast.DeclStmt , specs []dst.Spec ) {
584
+ if len (specs ) != 1 {
585
+ return
586
+ }
587
+
588
+ dds , _ := p .decorator .Dst .Nodes [node ].(* dst.DeclStmt )
589
+
590
+ if spec , ok := specs [0 ].(* dst.ValueSpec ); ok {
591
+ if spec .Decs .NodeDecs .Start == nil && spec .Decs .NodeDecs .End == nil {
592
+ spec .Decs .NodeDecs = dds .Decs .NodeDecs
593
+ dds .Decs .NodeDecs = dst.NodeDecs {}
594
+ }
595
+ }
596
+ }
597
+
565
598
seenDeclStmtIdx = i
566
599
for seenDeclStmtIdx < len (statements ) {
567
600
// If next statement is not a decl there's nothing more to
@@ -586,22 +619,68 @@ func (p *processor) parseBlockStatements(statements []ast.Stmt) {
586
619
break
587
620
}
588
621
589
- previousGenDecl .Specs = append (previousGenDecl .Specs , nextGenDecl .Specs ... )
590
- previousDeclNode .Decl = previousGenDecl
622
+ pd , _ := p .decorator .Dst .Nodes [previousGenDecl ].(* dst.GenDecl )
623
+ nd , _ := p .decorator .Dst .Nodes [nextGenDecl ].(* dst.GenDecl )
624
+
625
+ // We must set Rparen to true on the first node to indicate that
626
+ // it's a multiline block and that the comment should end with
627
+ // the assignment, not the closing parenthesis.
628
+ pd .Rparen = true
629
+
630
+ moveCommentsIfGrouped (previousDeclNode , pd .Specs )
631
+ moveCommentsIfGrouped (nextStatement , nd .Specs )
632
+
633
+ pd .Specs = append (pd .Specs , nd .Specs ... )
634
+
635
+ pn , _ := p .decorator .Dst .Nodes [previousDeclNode ].(* dst.DeclStmt )
636
+ pn .Decl = pd
591
637
592
638
endNode = nextStatement
593
639
seenDeclStmtIdx ++
594
640
}
595
641
642
+ // More hacks to ensure new text spans the whole way. If we have a
643
+ // comment on our end node that starts after the node ends it's a
644
+ // trailing comment so we need to span over it wit our new text.
645
+ //
646
+ // var a = 1
647
+ // var b = 2 // comment
648
+ // -------------------^ Need to end here
649
+ commentMap := ast .NewCommentMap (p .fileSet , endNode , p .file .Comments )
650
+ if cm , ok := commentMap [endNode ]; ok {
651
+ lastComment := cm [len (cm )- 1 ]
652
+ if lastComment .Pos () > endNode .End () {
653
+ endNode = lastComment
654
+ }
655
+ }
656
+
657
+ decl , ok := p .decorator .Dst .Nodes [previousStatement ].(* dst.DeclStmt )
658
+ if ! ok {
659
+ p .addWarning ("failed to get node" , stmt .Pos (), stmt )
660
+ continue
661
+ }
662
+
663
+ dummyFile := & dst.File {
664
+ Name : dst .NewIdent ("dummy" ),
665
+ Decls : []dst.Decl {
666
+ decl .Decl ,
667
+ },
668
+ }
669
+
596
670
b := bytes.Buffer {}
597
- _ = printer .Fprint (& b , p .fileSet , previousDeclNode )
671
+ decorator .Fprint (& b , dummyFile )
672
+
673
+ stripLen := len ("package dummy\n \n " )
674
+ newText := b .Bytes ()
675
+ newText = newText [stripLen :]
676
+ newText = newText [:len (newText )- 1 ]
598
677
599
678
p .addErrorRangeWithFix (
600
679
previousDeclNode .Pos (),
601
680
previousDeclNode .Pos (),
602
681
endNode .End (),
603
682
reasonNeverCuddleDeclare ,
604
- b . Bytes () ,
683
+ newText ,
605
684
)
606
685
607
686
case * ast.ExprStmt :
0 commit comments