Updates
Signed-off-by: jolheiser <john.olheiser@gmail.com>main v0.0.1
parent
c75ed4762a
commit
81144edfe4
|
@ -2,4 +2,5 @@
|
|||
.idea/
|
||||
|
||||
# Binaries
|
||||
/imp*
|
||||
/imp
|
||||
/imp.exe
|
|
@ -0,0 +1,25 @@
|
|||
builds:
|
||||
- env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- linux
|
||||
- windows
|
||||
- darwin
|
||||
ldflags:
|
||||
- "-s -w -X main.Version={{.Version}}"
|
||||
archives:
|
||||
- replacements:
|
||||
386: i386
|
||||
amd64: x86_64
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
release:
|
||||
gitea:
|
||||
owner: jolheiser
|
||||
name: imp
|
||||
gitea_urls:
|
||||
api: https://git.jojodev.com/api/v1/
|
||||
download: https://git.jojodev.com
|
|
@ -0,0 +1,38 @@
|
|||
clone:
|
||||
git:
|
||||
image: woodpeckerci/plugin-git
|
||||
settings:
|
||||
tags: true
|
||||
|
||||
pipeline:
|
||||
compliance:
|
||||
image: golang:1.18
|
||||
commands:
|
||||
- go test -race ./...
|
||||
- go vet ./...
|
||||
when:
|
||||
event: pull_request
|
||||
|
||||
build:
|
||||
image: goreleaser/goreleaser
|
||||
commands:
|
||||
- goreleaser build --snapshot
|
||||
when:
|
||||
event: pull_request
|
||||
|
||||
release:
|
||||
image: goreleaser/goreleaser
|
||||
commands:
|
||||
- goreleaser release
|
||||
secrets: [ gitea_token ]
|
||||
when:
|
||||
event: tag
|
||||
|
||||
prune:
|
||||
image: jolheiser/drone-gitea-prune
|
||||
settings:
|
||||
base: https://git.jojodev.com
|
||||
token:
|
||||
from_secret: gitea_token
|
||||
when:
|
||||
event: tag
|
21
Makefile
21
Makefile
|
@ -1,21 +0,0 @@
|
|||
GO ?= go
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
$(GO) build
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
$(GO) fmt ./...
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
$(GO) test -race ./...
|
||||
|
||||
.PHONY: vet
|
||||
vet:
|
||||
$(GO) vet ./...
|
||||
|
||||
.PHONY: imp
|
||||
imp: build
|
||||
./imp -w
|
|
@ -16,15 +16,10 @@ import (
|
|||
imp includes three flags:
|
||||
|
||||
* `--write` will write out the formatting rather than printing
|
||||
* `--imp-ignore` reads a `.impignore` file for globs to include/exclude (`.impignore` by default)
|
||||
* `--verbose` will print out extended information
|
||||
* `--ignore` reads a `.impignore` file for globs to include/exclude (`.impignore` by default)
|
||||
|
||||
|
||||
### `.impignore` format
|
||||
|
||||
The `.impignore` file follows a [globber format](https://gitea.com/jolheiser/globber),
|
||||
which closely resembles a traditional `.gitignore` file.
|
||||
|
||||
### Example
|
||||
|
||||
https://gitea.com/jolheiser/sip/pulls/17
|
|
@ -0,0 +1,40 @@
|
|||
package format
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
importRe = regexp.MustCompile(`(?ms)import \(([^)]+)\)`)
|
||||
otherRe = regexp.MustCompile(`(?:var|const|func)\s`)
|
||||
ErrNoImports = errors.New("no imports found")
|
||||
)
|
||||
|
||||
// Source formats a given src's imports
|
||||
func Source(src []byte, module string) ([]byte, error) {
|
||||
importStart := importRe.FindIndex(src)
|
||||
if importStart == nil {
|
||||
return nil, ErrNoImports
|
||||
}
|
||||
|
||||
otherStart := otherRe.FindIndex(src)
|
||||
if otherStart != nil && otherStart[0] < importStart[0] {
|
||||
return nil, ErrNoImports
|
||||
}
|
||||
|
||||
groups := importRe.FindStringSubmatch(string(src))
|
||||
if groups[0] == "" {
|
||||
return nil, ErrNoImports
|
||||
}
|
||||
|
||||
imports := strings.Split(groups[1], "\n")
|
||||
for idx, i := range imports {
|
||||
imports[idx] = strings.TrimSpace(i)
|
||||
}
|
||||
|
||||
block := parseBlock(module, imports)
|
||||
replaced := strings.Replace(string(src), groups[0], block.String(), 1)
|
||||
return []byte(replaced), nil
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package format
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/matryer/is"
|
||||
)
|
||||
|
||||
func TestSource(t *testing.T) {
|
||||
module := "foo.bar"
|
||||
|
||||
assert := is.New(t)
|
||||
|
||||
_, err := Source(noImports, module)
|
||||
assert.True(errors.Is(err, ErrNoImports)) // Should get error for no imports
|
||||
|
||||
_, err = Source(fakeImports, module)
|
||||
assert.True(errors.Is(err, ErrNoImports)) // Should get error for no "real" imports
|
||||
|
||||
formatted, err := Source(before, module)
|
||||
assert.NoErr(err) // Should be able to format before block
|
||||
assert.True(bytes.Equal(formatted, after)) // Formatted should match after block
|
||||
}
|
||||
|
||||
var (
|
||||
noImports = []byte(`package main
|
||||
|
||||
func main() {}`)
|
||||
fakeImports = []byte(`package main
|
||||
|
||||
func main() {
|
||||
s := "import \"fmt\""
|
||||
_ = s
|
||||
}`)
|
||||
before = []byte(`package main
|
||||
|
||||
import (
|
||||
"foo.bar/baz"
|
||||
"net/http"
|
||||
"github.com/peterbourgon/ff/v3"
|
||||
"io"
|
||||
"os"
|
||||
"github.com/matryer/is"
|
||||
"foo.bar/bux"
|
||||
)
|
||||
|
||||
func main() {}`)
|
||||
after = []byte(`package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"foo.bar/baz"
|
||||
"foo.bar/bux"
|
||||
|
||||
"github.com/matryer/is"
|
||||
"github.com/peterbourgon/ff/v3"
|
||||
)
|
||||
|
||||
func main() {}`)
|
||||
)
|
|
@ -0,0 +1,107 @@
|
|||
package format
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type importItem struct {
|
||||
Comment string
|
||||
Name string
|
||||
Path string
|
||||
}
|
||||
|
||||
func (ii importItem) String() string {
|
||||
var comment string
|
||||
if ii.Comment != "" {
|
||||
comment = ii.Comment + "\n\t"
|
||||
}
|
||||
var name string
|
||||
if ii.Name != "" {
|
||||
name = ii.Name + " "
|
||||
}
|
||||
return comment + name + ii.Path
|
||||
}
|
||||
|
||||
type importBlock struct {
|
||||
Stdlib []importItem
|
||||
Module []importItem
|
||||
Other []importItem
|
||||
}
|
||||
|
||||
func importItemSlice(ii []importItem) []string {
|
||||
iis := make([]string, 0, len(ii))
|
||||
for _, i := range ii {
|
||||
iis = append(iis, i.String())
|
||||
}
|
||||
return iis
|
||||
}
|
||||
|
||||
func (i importBlock) String() string {
|
||||
var decl strings.Builder
|
||||
|
||||
decl.WriteString(strings.Join(importItemSlice(i.Stdlib), "\n\t"))
|
||||
if len(i.Stdlib) > 0 {
|
||||
decl.WriteString("\n\n\t")
|
||||
}
|
||||
decl.WriteString(strings.Join(importItemSlice(i.Module), "\n\t"))
|
||||
if len(i.Module) > 0 {
|
||||
decl.WriteString("\n\n\t")
|
||||
}
|
||||
decl.WriteString(strings.Join(importItemSlice(i.Other), "\n\t"))
|
||||
|
||||
return fmt.Sprintf(`import (
|
||||
%s
|
||||
)`, decl.String())
|
||||
}
|
||||
|
||||
func parseBlock(module string, imports []string) importBlock {
|
||||
var block importBlock
|
||||
var comment string
|
||||
for _, imp := range imports {
|
||||
if imp == "" {
|
||||
continue
|
||||
}
|
||||
i := imp
|
||||
if strings.HasPrefix(i, `//`) {
|
||||
var nl string
|
||||
if comment != "" {
|
||||
nl = "\n"
|
||||
}
|
||||
comment = nl + i
|
||||
continue
|
||||
}
|
||||
|
||||
var name string
|
||||
if !strings.HasPrefix(i, `"`) {
|
||||
parts := strings.SplitN(i, " ", 2)
|
||||
name = parts[0]
|
||||
i = parts[1]
|
||||
}
|
||||
item := importItem{
|
||||
Comment: comment,
|
||||
Name: name,
|
||||
Path: i,
|
||||
}
|
||||
switch {
|
||||
case strings.Contains(i, module):
|
||||
block.Module = append(block.Module, item)
|
||||
case strings.Contains(i, "."):
|
||||
block.Other = append(block.Other, item)
|
||||
default:
|
||||
block.Stdlib = append(block.Stdlib, item)
|
||||
}
|
||||
comment = ""
|
||||
}
|
||||
|
||||
fn := func(ii []importItem) func(int, int) bool {
|
||||
return func(i, j int) bool {
|
||||
return ii[i].Path < ii[j].Path
|
||||
}
|
||||
}
|
||||
sort.Slice(block.Stdlib, fn(block.Stdlib))
|
||||
sort.Slice(block.Module, fn(block.Module))
|
||||
sort.Slice(block.Other, fn(block.Other))
|
||||
return block
|
||||
}
|
13
go.mod
13
go.mod
|
@ -1,11 +1,14 @@
|
|||
module gitea.com/jolheiser/imp
|
||||
|
||||
go 1.14
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
gitea.com/jolheiser/globber v0.0.1
|
||||
github.com/gobuffalo/here v0.6.2
|
||||
github.com/urfave/cli/v2 v2.2.0
|
||||
go.jolheiser.com/beaver v1.0.2
|
||||
go.jolheiser.com/regexp v0.1.1
|
||||
github.com/gobuffalo/here v0.6.6
|
||||
github.com/matryer/is v1.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
43
go.sum
43
go.sum
|
@ -1,40 +1,31 @@
|
|||
gitea.com/jolheiser/globber v0.0.1 h1:BVoc4Ml9N3ssix5Xy32gpQOO1jnAuzMsYc5h/LP7/jA=
|
||||
gitea.com/jolheiser/globber v0.0.1/go.mod h1:vr1VkO/biaYgeGj8tRD6SMAWhsqINKFYl9en3YbpQvc=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gobuffalo/here v0.6.2 h1:ZtCqC7F9ou3moLbYfHM1Tj+gwHGgWhjyRjVjsir9BE0=
|
||||
github.com/gobuffalo/here v0.6.2/go.mod h1:D75Sq0p2BVHdgQu3vCRsXbg85rx943V19urJpqAVWjI=
|
||||
github.com/gobuffalo/here v0.6.6 h1:/o+jfSwe36pKQ577grsXGoMYql/zheiGwg1XFo3CBJU=
|
||||
github.com/gobuffalo/here v0.6.6/go.mod h1:C4JZL5PsXWKzP/CAchaIzuUWlaae6CaAiMYQ0ieY62M=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
|
||||
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/urfave/cli/v2 v2.2.0 h1:JTTnM6wKzdA0Jqodd966MVj4vWbbquZykeX1sKbe2C4=
|
||||
github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
||||
go.jolheiser.com/beaver v1.0.2 h1:KA2D6iO8MQhZi1nZYi/Chak/f1Cxfrs6b1XO623+Khk=
|
||||
go.jolheiser.com/beaver v1.0.2/go.mod h1:7X4F5+XOGSC3LejTShoBdqtRCnPWcnRgmYGmG3EKW8g=
|
||||
go.jolheiser.com/regexp v0.1.1 h1:nMCzilEL/oIcZJwnRT4bb+FNxaeAFBdqSjq7rgoNSGg=
|
||||
go.jolheiser.com/regexp v0.1.1/go.mod h1:58uCpYxGy/DbqVRuo6oU93kh3B3GeyK5eJznQvy2N6c=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
138
imp.go
138
imp.go
|
@ -1,50 +1,35 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"gitea.com/jolheiser/imp/format"
|
||||
|
||||
"gitea.com/jolheiser/globber"
|
||||
"github.com/gobuffalo/here"
|
||||
"github.com/urfave/cli/v2"
|
||||
"go.jolheiser.com/beaver"
|
||||
"go.jolheiser.com/beaver/color"
|
||||
"go.jolheiser.com/regexp"
|
||||
)
|
||||
|
||||
var (
|
||||
module string
|
||||
importRe = regexp.MustCompile(`(?ms)import \(([^)]+)\)`)
|
||||
otherRe = regexp.MustCompile(`(?:var|const|func)\s`)
|
||||
)
|
||||
|
||||
func runImp(ctx *cli.Context) error {
|
||||
if ctx.Bool("verbose") {
|
||||
beaver.Console.Level = beaver.DEBUG
|
||||
}
|
||||
func runImp(root, ignore string, write bool) error {
|
||||
info, err := here.Current()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
module = info.Module.Path
|
||||
beaver.Debugf("Current module: %s", module)
|
||||
|
||||
globs, err := globber.ParseFile(ctx.String("imp-ignore"))
|
||||
module := info.Module.Path
|
||||
globs, err := globber.ParseFile(ignore)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
if !errors.Is(err, fs.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
globs = globber.New()
|
||||
}
|
||||
|
||||
var failed bool
|
||||
diffColor := color.New(color.FgGreen, color.BgHiBlack)
|
||||
if err := filepath.Walk(".", func(walkPath string, walkInfo os.FileInfo, walkErr error) error {
|
||||
if err := filepath.Walk(root, func(walkPath string, walkInfo os.FileInfo, walkErr error) error {
|
||||
if walkErr != nil {
|
||||
return walkErr
|
||||
}
|
||||
|
@ -58,43 +43,21 @@ func runImp(ctx *cli.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
beaver.Debugf("Checking file %s", walkPath)
|
||||
data, err := ioutil.ReadFile(walkPath)
|
||||
data, err := os.ReadFile(walkPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
formatted, err := format.Source(data, module)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
importStart := importRe.FindIndex(data)
|
||||
if importStart == nil {
|
||||
return nil
|
||||
if write {
|
||||
return os.WriteFile(walkPath, formatted, walkInfo.Mode())
|
||||
}
|
||||
|
||||
otherStart := otherRe.FindIndex(data)
|
||||
if otherStart != nil && otherStart[0] < importStart[0] {
|
||||
return nil
|
||||
}
|
||||
|
||||
groups := importRe.Groups(string(data))
|
||||
if groups.Index(0) == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
imports := strings.Split(groups.Index(1), "\n")
|
||||
for idx, i := range imports {
|
||||
imports[idx] = strings.TrimSpace(i)
|
||||
}
|
||||
|
||||
formatted := formatImportStmt(splitImports(imports))
|
||||
if ctx.Bool("write") {
|
||||
replaced := strings.Replace(string(data), groups.Index(0), formatted, 1)
|
||||
return ioutil.WriteFile(walkPath, []byte(replaced), walkInfo.Mode())
|
||||
}
|
||||
|
||||
if !strings.EqualFold(groups.Index(0), formatted) {
|
||||
if !bytes.Equal(data, formatted) {
|
||||
failed = true
|
||||
beaver.Infof("File: %s", diffColor.Format(walkPath))
|
||||
beaver.Infof("Expected:\n%s", diffColor.Format(formatted))
|
||||
beaver.Infof("Got:\n%s\n", diffColor.Format(groups.Index(0)))
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
|
@ -107,7 +70,6 @@ func runImp(ctx *cli.Context) error {
|
|||
}
|
||||
|
||||
func checkSkip(walkPath string, walkInfo os.FileInfo, globs *globber.GlobSet) walkStatus {
|
||||
|
||||
// Skip current directory
|
||||
if strings.EqualFold(walkPath, ".") {
|
||||
return SKIP
|
||||
|
@ -115,7 +77,6 @@ func checkSkip(walkPath string, walkInfo os.FileInfo, globs *globber.GlobSet) wa
|
|||
|
||||
// Skip hidden paths (starting with ".")
|
||||
if strings.HasPrefix(walkPath, ".") {
|
||||
beaver.Debugf("Skipping hidden path %s", walkPath)
|
||||
if walkInfo.IsDir() {
|
||||
return SKIPDIR
|
||||
}
|
||||
|
@ -129,75 +90,22 @@ func checkSkip(walkPath string, walkInfo os.FileInfo, globs *globber.GlobSet) wa
|
|||
|
||||
// Skip non-Go files
|
||||
if !strings.HasSuffix(walkInfo.Name(), ".go") {
|
||||
beaver.Debugf("Skipping non-Go file %s", walkPath)
|
||||
return SKIP
|
||||
}
|
||||
|
||||
// Skip included (ignored) globs
|
||||
i, e := globs.Explain(walkPath)
|
||||
if len(i) > 0 && len(e) == 0 {
|
||||
beaver.Debugf("Skipping file %s because of .impignore rule %s", walkPath, i[0].Pattern)
|
||||
return SKIP
|
||||
}
|
||||
|
||||
return CHECK
|
||||
}
|
||||
|
||||
func splitImports(imports []string) []importList {
|
||||
// 0 -> stdlib
|
||||
// 1 -> this module
|
||||
// 2 -> others
|
||||
split := []importList{{}, {}, {}}
|
||||
var comment string
|
||||
for _, imp := range imports {
|
||||
if imp == "" {
|
||||
continue
|
||||
}
|
||||
i := imp
|
||||
if strings.HasPrefix(i, `//`) {
|
||||
var nl string
|
||||
if comment != "" {
|
||||
nl = "\n"
|
||||
}
|
||||
comment = nl + i
|
||||
continue
|
||||
}
|
||||
type walkStatus int
|
||||
|
||||
var name string
|
||||
if !strings.HasPrefix(i, `"`) {
|
||||
parts := strings.SplitN(i, " ", 2)
|
||||
name = parts[0]
|
||||
i = parts[1]
|
||||
}
|
||||
switch {
|
||||
case strings.HasPrefix(i, `"`+module):
|
||||
split[1] = append(split[1], importItem{comment, name, i})
|
||||
case strings.Contains(i, "."):
|
||||
split[2] = append(split[2], importItem{comment, name, i})
|
||||
default:
|
||||
split[0] = append(split[0], importItem{comment, name, i})
|
||||
}
|
||||
comment = ""
|
||||
}
|
||||
|
||||
for _, s := range split {
|
||||
sort.Sort(s)
|
||||
}
|
||||
return split
|
||||
}
|
||||
|
||||
func formatImportStmt(imports []importList) string {
|
||||
var decl string
|
||||
for _, imp := range imports {
|
||||
var pre string
|
||||
if decl != "" {
|
||||
pre = "\n\n\t"
|
||||
}
|
||||
if len(imp) > 0 {
|
||||
decl += pre + strings.Join(imp.StringSlice(), "\n\t")
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf(`import (
|
||||
%s
|
||||
)`, decl)
|
||||
}
|
||||
const (
|
||||
SKIP walkStatus = iota
|
||||
SKIPDIR
|
||||
CHECK
|
||||
)
|
||||
|
|
49
main.go
49
main.go
|
@ -1,37 +1,30 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
"go.jolheiser.com/beaver"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "Imp"
|
||||
app.Usage = "Re-order imports"
|
||||
app.Flags = []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "write",
|
||||
Aliases: []string{"w"},
|
||||
Usage: "Write the re-ordered imports instead of just printing them",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "verbose",
|
||||
Aliases: []string{"v"},
|
||||
Usage: "Print more information",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "imp-ignore",
|
||||
Aliases: []string{"i"},
|
||||
Usage: "Path to a .impignore file for globs",
|
||||
Value: ".impignore",
|
||||
},
|
||||
}
|
||||
app.Action = runImp
|
||||
var Version = "develop"
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
beaver.Fatal(err)
|
||||
func main() {
|
||||
fs := flag.NewFlagSet("imp", flag.ExitOnError)
|
||||
writeFlag := fs.Bool("write", false, "Write the re-ordered imports instead of just printing them")
|
||||
fs.BoolVar(writeFlag, "w", *writeFlag, "--write")
|
||||
ignoreFlag := fs.String("ignore", ".impignore", "Path to .impignore file")
|
||||
fs.StringVar(ignoreFlag, "i", *ignoreFlag, "--ignore")
|
||||
|
||||
if err := fs.Parse(os.Args[1:]); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
root := "."
|
||||
if fs.NArg() > 0 {
|
||||
root = fs.Arg(0)
|
||||
}
|
||||
|
||||
if err := runImp(root, *ignoreFlag, *writeFlag); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
|
|
49
struct.go
49
struct.go
|
@ -1,49 +0,0 @@
|
|||
package main
|
||||
|
||||
type importItem struct {
|
||||
Comment string
|
||||
Name string
|
||||
Path string
|
||||
}
|
||||
|
||||
func (ii importItem) String() string {
|
||||
var comment string
|
||||
if ii.Comment != "" {
|
||||
comment = ii.Comment + "\n\t"
|
||||
}
|
||||
var name string
|
||||
if ii.Name != "" {
|
||||
name = ii.Name + " "
|
||||
}
|
||||
return comment + name + ii.Path
|
||||
}
|
||||
|
||||
type importList []importItem
|
||||
|
||||
func (il importList) Len() int {
|
||||
return len(il)
|
||||
}
|
||||
|
||||
func (il importList) Less(i, j int) bool {
|
||||
return il[i].Path < il[j].Path
|
||||
}
|
||||
|
||||
func (il importList) Swap(i, j int) {
|
||||
il[i], il[j] = il[j], il[i]
|
||||
}
|
||||
|
||||
func (il importList) StringSlice() []string {
|
||||
s := make([]string, len(il))
|
||||
for idx, ii := range il {
|
||||
s[idx] = ii.String()
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
type walkStatus int
|
||||
|
||||
const (
|
||||
SKIP walkStatus = iota
|
||||
SKIPDIR
|
||||
CHECK
|
||||
)
|
Reference in New Issue