@@ -11,6 +11,7 @@ import (
11
11
"go/constant"
12
12
"go/format"
13
13
"go/parser"
14
+ "go/printer"
14
15
"go/token"
15
16
"go/types"
16
17
pathpkg "path"
@@ -202,17 +203,37 @@ func (st *state) inline() (*Result, error) {
202
203
}
203
204
}
204
205
206
+ // File rewriting. This proceeds in multiple passes, in order to maximally
207
+ // preserve comment positioning. (This could be greatly simplified once
208
+ // comments are stored in the tree.)
209
+ //
205
210
// Don't call replaceNode(caller.File, res.old, res.new)
206
211
// as it mutates the caller's syntax tree.
207
212
// Instead, splice the file, replacing the extent of the "old"
208
213
// node by a formatting of the "new" node, and re-parse.
209
214
// We'll fix up the imports on this new tree, and format again.
210
- var f * ast.File
215
+ //
216
+ // Inv: f is the result of parsing content, using fset.
217
+ var (
218
+ content = caller .Content
219
+ fset = caller .Fset
220
+ f * ast.File // parsed below
221
+ )
222
+ reparse := func () error {
223
+ const mode = parser .ParseComments | parser .SkipObjectResolution | parser .AllErrors
224
+ f , err = parser .ParseFile (fset , "callee.go" , content , mode )
225
+ if err != nil {
226
+ // Something has gone very wrong.
227
+ logf ("failed to reparse <<%s>>" , string (content )) // debugging
228
+ return err
229
+ }
230
+ return nil
231
+ }
211
232
{
212
- start := offsetOf (caller . Fset , res .old .Pos ())
213
- end := offsetOf (caller . Fset , res .old .End ())
233
+ start := offsetOf (fset , res .old .Pos ())
234
+ end := offsetOf (fset , res .old .End ())
214
235
var out bytes.Buffer
215
- out .Write (caller . Content [:start ])
236
+ out .Write (content [:start ])
216
237
// TODO(adonovan): might it make more sense to use
217
238
// callee.Fset when formatting res.new?
218
239
// The new tree is a mix of (cloned) caller nodes for
@@ -232,21 +253,18 @@ func (st *state) inline() (*Result, error) {
232
253
if i > 0 {
233
254
out .WriteByte ('\n' )
234
255
}
235
- if err := format .Node (& out , caller . Fset , stmt ); err != nil {
256
+ if err := format .Node (& out , fset , stmt ); err != nil {
236
257
return nil , err
237
258
}
238
259
}
239
260
} else {
240
- if err := format .Node (& out , caller . Fset , res .new ); err != nil {
261
+ if err := format .Node (& out , fset , res .new ); err != nil {
241
262
return nil , err
242
263
}
243
264
}
244
- out .Write (caller .Content [end :])
245
- const mode = parser .ParseComments | parser .SkipObjectResolution | parser .AllErrors
246
- f , err = parser .ParseFile (caller .Fset , "callee.go" , & out , mode )
247
- if err != nil {
248
- // Something has gone very wrong.
249
- logf ("failed to parse <<%s>>" , & out ) // debugging
265
+ out .Write (content [end :])
266
+ content = out .Bytes ()
267
+ if err := reparse (); err != nil {
250
268
return nil , err
251
269
}
252
270
}
@@ -257,15 +275,58 @@ func (st *state) inline() (*Result, error) {
257
275
// to avoid migration of pre-import comments.
258
276
// The imports will be organized below.
259
277
if len (res .newImports ) > 0 {
260
- var importDecl * ast.GenDecl
278
+ // If we have imports to add, do so independent of the rest of the file.
279
+ // Otherwise, the length of the new imports may consume floating comments,
280
+ // causing them to be printed inside the imports block.
281
+ var (
282
+ importDecl * ast.GenDecl
283
+ comments []* ast.CommentGroup // relevant comments.
284
+ before , after []byte // pre- and post-amble for the imports block.
285
+ )
261
286
if len (f .Imports ) > 0 {
262
287
// Append specs to existing import decl
263
288
importDecl = f .Decls [0 ].(* ast.GenDecl )
289
+ for _ , comment := range f .Comments {
290
+ // Filter comments. Don't use CommentMap.Filter here, because we don't
291
+ // want to include comments that document the import decl itself, for
292
+ // example:
293
+ //
294
+ // // We don't want this comment to be duplicated.
295
+ // import (
296
+ // "something"
297
+ // )
298
+ if importDecl .Pos () <= comment .Pos () && comment .Pos () < importDecl .End () {
299
+ comments = append (comments , comment )
300
+ }
301
+ }
302
+ before = content [:offsetOf (fset , importDecl .Pos ())]
303
+ importDecl .Doc = nil // present in before
304
+ after = content [offsetOf (fset , importDecl .End ()):]
264
305
} else {
265
306
// Insert new import decl.
266
307
importDecl = & ast.GenDecl {Tok : token .IMPORT }
267
308
f .Decls = prepend [ast.Decl ](importDecl , f .Decls ... )
309
+
310
+ // Make room for the new declaration after the package declaration.
311
+ pkgEnd := f .Name .End ()
312
+ file := fset .File (pkgEnd )
313
+ if file == nil {
314
+ logf ("internal error: missing pkg file" )
315
+ return nil , fmt .Errorf ("missing pkg file for %s" , f .Name .Name )
316
+ }
317
+ // Preserve any comments after the package declaration, by splicing in
318
+ // the new import block after the end of the package declaration line.
319
+ line := file .Line (pkgEnd )
320
+ if line < len (file .Lines ()) { // line numbers are 1-based
321
+ nextLinePos := file .LineStart (line + 1 )
322
+ nextLine := offsetOf (fset , nextLinePos )
323
+ before = slices .Concat (content [:nextLine ], []byte ("\n " ))
324
+ after = slices .Concat ([]byte ("\n \n " ), content [nextLine :])
325
+ } else {
326
+ before = slices .Concat (content , []byte ("\n \n " ))
327
+ }
268
328
}
329
+ // Add new imports.
269
330
for _ , imp := range res .newImports {
270
331
// Check that the new imports are accessible.
271
332
path , _ := strconv .Unquote (imp .spec .Path .Value )
@@ -274,6 +335,21 @@ func (st *state) inline() (*Result, error) {
274
335
}
275
336
importDecl .Specs = append (importDecl .Specs , imp .spec )
276
337
}
338
+ var out bytes.Buffer
339
+ out .Write (before )
340
+ commented := & printer.CommentedNode {
341
+ Node : importDecl ,
342
+ Comments : comments ,
343
+ }
344
+ if err := format .Node (& out , fset , commented ); err != nil {
345
+ logf ("failed to format new importDecl: %v" , err ) // debugging
346
+ return nil , err
347
+ }
348
+ out .Write (after )
349
+ content = out .Bytes ()
350
+ if err := reparse (); err != nil {
351
+ return nil , err
352
+ }
277
353
}
278
354
279
355
// Delete imports referenced only by caller.Call.Fun.
0 commit comments