github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/src/pkg/go/ast/import.go (about)

     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.IsValid() {
    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() }