src/pkg/go/ast/import.go - The Go Programming Language

Golang

Source file src/pkg/go/ast/import.go

     1	// Copyright 2011 The Go Authors. All rights reserved.
     2	// Use of this source code is governed by a BSD-style
     3	// license that can be found in the LICENSE file.
     4	
     5	package ast
     6	
     7	import (
     8		"go/token"
     9		"sort"
    10		"strconv"
    11	)
    12	
    13	// SortImports sorts runs of consecutive import lines in import blocks in f.
    14	func SortImports(fset *token.FileSet, f *File) {
    15		for _, d := range f.Decls {
    16			d, ok := d.(*GenDecl)
    17			if !ok || d.Tok != token.IMPORT {
    18				// Not an import declaration, so we're done.
    19				// Imports are always first.
    20				break
    21			}
    22	
    23			if d.Lparen == token.NoPos {
    24				// Not a block: sorted by default.
    25				continue
    26			}
    27	
    28			// Identify and sort runs of specs on successive lines.
    29			i := 0
    30			for j, s := range d.Specs {
    31				if j > i && fset.Position(s.Pos()).Line > 1+fset.Position(d.Specs[j-1].End()).Line {
    32					// j begins a new run.  End this one.
    33					sortSpecs(fset, f, d.Specs[i:j])
    34					i = j
    35				}
    36			}
    37			sortSpecs(fset, f, d.Specs[i:])
    38		}
    39	}
    40	
    41	func importPath(s Spec) string {
    42		t, err := strconv.Unquote(s.(*ImportSpec).Path.Value)
    43		if err == nil {
    44			return t
    45		}
    46		return ""
    47	}
    48	
    49	type posSpan struct {
    50		Start token.Pos
    51		End   token.Pos
    52	}
    53	
    54	func sortSpecs(fset *token.FileSet, f *File, specs []Spec) {
    55		// Avoid work if already sorted (also catches < 2 entries).
    56		sorted := true
    57		for i, s := range specs {
    58			if i > 0 && importPath(specs[i-1]) > importPath(s) {
    59				sorted = false
    60				break
    61			}
    62		}
    63		if sorted {
    64			return
    65		}
    66	
    67		// Record positions for specs.
    68		pos := make([]posSpan, len(specs))
    69		for i, s := range specs {
    70			pos[i] = posSpan{s.Pos(), s.End()}
    71		}
    72	
    73		// Identify comments in this range.
    74		// Any comment from pos[0].Start to the final line counts.
    75		lastLine := fset.Position(pos[len(pos)-1].End).Line
    76		cstart := len(f.Comments)
    77		cend := len(f.Comments)
    78		for i, g := range f.Comments {
    79			if g.Pos() < pos[0].Start {
    80				continue
    81			}
    82			if i < cstart {
    83				cstart = i
    84			}
    85			if fset.Position(g.End()).Line > lastLine {
    86				cend = i
    87				break
    88			}
    89		}
    90		comments := f.Comments[cstart:cend]
    91	
    92		// Assign each comment to the import spec preceding it.
    93		importComment := map[*ImportSpec][]*CommentGroup{}
    94		specIndex := 0
    95		for _, g := range comments {
    96			for specIndex+1 < len(specs) && pos[specIndex+1].Start <= g.Pos() {
    97				specIndex++
    98			}
    99			s := specs[specIndex].(*ImportSpec)
   100			importComment[s] = append(importComment[s], g)
   101		}
   102	
   103		// Sort the import specs by import path.
   104		// Reassign the import paths to have the same position sequence.
   105		// Reassign each comment to abut the end of its spec.
   106		// Sort the comments by new position.
   107		sort.Sort(byImportPath(specs))
   108		for i, s := range specs {
   109			s := s.(*ImportSpec)
   110			if s.Name != nil {
   111				s.Name.NamePos = pos[i].Start
   112			}
   113			s.Path.ValuePos = pos[i].Start
   114			s.EndPos = pos[i].End
   115			for _, g := range importComment[s] {
   116				for _, c := range g.List {
   117					c.Slash = pos[i].End
   118				}
   119			}
   120		}
   121		sort.Sort(byCommentPos(comments))
   122	}
   123	
   124	type byImportPath []Spec // slice of *ImportSpec
   125	
   126	func (x byImportPath) Len() int           { return len(x) }
   127	func (x byImportPath) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
   128	func (x byImportPath) Less(i, j int) bool { return importPath(x[i]) < importPath(x[j]) }
   129	
   130	type byCommentPos []*CommentGroup
   131	
   132	func (x byCommentPos) Len() int           { return len(x) }
   133	func (x byCommentPos) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
   134	func (x byCommentPos) Less(i, j int) bool { return x[i].Pos() < x[j].Pos() }