From 55748a8dbab374852d2a86c6c57d39330c245e73 Mon Sep 17 00:00:00 2001
From: Norman Gehrsitz <norman.gehrsitz@gmx.de>
Date: Mon, 31 Jan 2022 13:58:30 +0100
Subject: [PATCH 1/5] Add support for gci sections

Signed-off-by: Norman Gehrsitz <norman.gehrsitz@gmx.de>
---
 .golangci.example.yml          | 16 ++++--
 .golangci.yml                  |  2 -
 go.mod                         |  2 +-
 go.sum                         |  8 +--
 pkg/config/linters_settings.go |  5 +-
 pkg/golinters/gci.go           | 99 +++++++---------------------------
 pkg/golinters/gofmt_common.go  | 14 +++--
 pkg/lint/lintersdb/manager.go  |  4 +-
 test/linters_test.go           | 16 +++---
 test/testdata/configs/gci.yml  |  6 +++
 test/testdata/gci.go           |  2 +
 test/testdata/gci/gci.go       |  3 +-
 12 files changed, 67 insertions(+), 110 deletions(-)
 create mode 100644 test/testdata/configs/gci.yml

diff --git a/.golangci.example.yml b/.golangci.example.yml
index 0e17ee939807..15022f2ec31d 100644
--- a/.golangci.example.yml
+++ b/.golangci.example.yml
@@ -295,10 +295,18 @@ linters-settings:
     statements: -1
 
   gci:
-    # Put imports beginning with prefix after 3rd-party packages.
-    # Only support one prefix.
-    # If not set, use `goimports.local-prefixes`.
-    local-prefixes: github.com/org/project
+    # Checks that no inline Comments are present
+    no-inlineComments: false
+    # Checks that no prefix Comments(comment lines above an import) are present
+    no-prefixComments: false
+    # Section configuration to compare against.
+    # Run gci print -h for a detailed explanation
+    sections:
+      - Standard
+      - Default
+    # Separators that should be present between sections
+    sectionSeparators:
+      - Newline
 
   gocognit:
     # Minimal code complexity to report
diff --git a/.golangci.yml b/.golangci.yml
index 3cc116d03bf8..a17d2b119bad 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -12,8 +12,6 @@ linters-settings:
   funlen:
     lines: 100
     statements: 50
-  gci:
-    local-prefixes: github.com/golangci/golangci-lint
   goconst:
     min-len: 2
     min-occurrences: 3
diff --git a/go.mod b/go.mod
index 01a1286bffd4..c12c4df76c05 100644
--- a/go.mod
+++ b/go.mod
@@ -19,7 +19,7 @@ require (
 	github.com/breml/errchkjson v0.2.1
 	github.com/butuzov/ireturn v0.1.1
 	github.com/charithe/durationcheck v0.0.9
-	github.com/daixiang0/gci v0.2.9
+	github.com/daixiang0/gci v0.3.0
 	github.com/denis-tingajkin/go-header v0.4.2
 	github.com/esimonov/ifshort v1.0.4
 	github.com/fatih/color v1.13.0
diff --git a/go.sum b/go.sum
index fadae35e04d9..0922ec987386 100644
--- a/go.sum
+++ b/go.sum
@@ -158,8 +158,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsr
 github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
-github.com/daixiang0/gci v0.2.9 h1:iwJvwQpBZmMg31w+QQ6jsyZ54KEATn6/nfARbBNW294=
-github.com/daixiang0/gci v0.2.9/go.mod h1:+4dZ7TISfSmqfAGv59ePaHfNzgGtIkHAhhdKggP1JAc=
+github.com/daixiang0/gci v0.3.0 h1:6x2xp99la0TfGmdDJ6T2VrLtCoZwYUVp4/5zT8J7+Go=
+github.com/daixiang0/gci v0.3.0/go.mod h1:jaASoJmv/ykO9dAAPy31iJnreV19248qKDdVWf3QgC4=
 github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 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=
@@ -419,6 +419,8 @@ github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOn
 github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
 github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
 github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
+github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
+github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo=
 github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
@@ -985,6 +987,7 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1171,7 +1174,6 @@ golang.org/x/tools v0.0.0-20201023174141-c8cfbd0f21e6/go.mod h1:emZCQorbCU4vsT4f
 golang.org/x/tools v0.0.0-20201028025901-8cd080b735b3/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.0.0-20201114224030-61ea331ec02b/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20201118003311-bd56c0adb394/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
diff --git a/pkg/config/linters_settings.go b/pkg/config/linters_settings.go
index 7e97ad3c5575..e538142a514d 100644
--- a/pkg/config/linters_settings.go
+++ b/pkg/config/linters_settings.go
@@ -253,7 +253,10 @@ type FunlenSettings struct {
 }
 
 type GciSettings struct {
-	LocalPrefixes string `mapstructure:"local-prefixes"`
+	NoInlineComments bool     `mapstructure:"no-inlineComments"`
+	NoPrefixComments bool     `mapstructure:"no-prefixComments"`
+	Sections         []string `mapstructure:"sections"`
+	SectionSeparator []string `mapstructure:"sectionSeparators"`
 }
 
 type GocognitSettings struct {
diff --git a/pkg/golinters/gci.go b/pkg/golinters/gci.go
index 9886fc5f2587..a54502af8624 100644
--- a/pkg/golinters/gci.go
+++ b/pkg/golinters/gci.go
@@ -1,96 +1,35 @@
 package golinters
 
 import (
-	"bytes"
-	"fmt"
-	"sync"
+	"strings"
 
-	"github.com/daixiang0/gci/pkg/gci"
-	"github.com/pkg/errors"
-	"github.com/shazow/go-diff/difflib"
+	gciAnalyzer "github.com/daixiang0/gci/pkg/analyzer"
 	"golang.org/x/tools/go/analysis"
 
+	"github.com/golangci/golangci-lint/pkg/config"
 	"github.com/golangci/golangci-lint/pkg/golinters/goanalysis"
-	"github.com/golangci/golangci-lint/pkg/lint/linter"
 )
 
 const gciName = "gci"
 
-func NewGci() *goanalysis.Linter {
-	var mu sync.Mutex
-	var resIssues []goanalysis.Issue
-	differ := difflib.New()
-
-	analyzer := &analysis.Analyzer{
-		Name: gciName,
-		Doc:  goanalysis.TheOnlyanalyzerDoc,
+func NewGci(settings *config.GciSettings) *goanalysis.Linter {
+	analyzer := gciAnalyzer.Analyzer
+	var cfg map[string]map[string]interface{}
+	if settings != nil {
+		cfg = map[string]map[string]interface{}{
+			analyzer.Name: {
+				gciAnalyzer.NoInlineCommentsFlag:  settings.NoInlineComments,
+				gciAnalyzer.NoPrefixCommentsFlag:  settings.NoPrefixComments,
+				gciAnalyzer.SectionsFlag:          strings.Join(settings.Sections, gciAnalyzer.SectionDelimiter),
+				gciAnalyzer.SectionSeparatorsFlag: strings.Join(settings.SectionSeparator, gciAnalyzer.SectionDelimiter),
+			},
+		}
 	}
+
 	return goanalysis.NewLinter(
 		gciName,
-		"Gci control golang package import order and make it always deterministic.",
+		"Gci controls golang package import order and makes it always deterministic.",
 		[]*analysis.Analyzer{analyzer},
-		nil,
-	).WithContextSetter(func(lintCtx *linter.Context) {
-		localFlag := lintCtx.Settings().Gci.LocalPrefixes
-		goimportsFlag := lintCtx.Settings().Goimports.LocalPrefixes
-		if localFlag == "" && goimportsFlag != "" {
-			localFlag = goimportsFlag
-		}
-
-		analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
-			var fileNames []string
-			for _, f := range pass.Files {
-				pos := pass.Fset.PositionFor(f.Pos(), false)
-				fileNames = append(fileNames, pos.Filename)
-			}
-
-			var issues []goanalysis.Issue
-
-			flagSet := gci.FlagSet{
-				LocalFlag: gci.ParseLocalFlag(localFlag),
-			}
-
-			for _, f := range fileNames {
-				source, result, err := gci.Run(f, &flagSet)
-				if err != nil {
-					return nil, err
-				}
-				if result == nil {
-					continue
-				}
-
-				diff := bytes.Buffer{}
-				_, err = diff.WriteString(fmt.Sprintf("--- %[1]s\n+++ %[1]s\n", f))
-				if err != nil {
-					return nil, fmt.Errorf("can't write diff header: %v", err)
-				}
-
-				err = differ.Diff(&diff, bytes.NewReader(source), bytes.NewReader(result))
-				if err != nil {
-					return nil, fmt.Errorf("can't get gci diff output: %v", err)
-				}
-
-				is, err := extractIssuesFromPatch(diff.String(), lintCtx.Log, lintCtx, gciName)
-				if err != nil {
-					return nil, errors.Wrapf(err, "can't extract issues from gci diff output %q", diff.String())
-				}
-
-				for i := range is {
-					issues = append(issues, goanalysis.NewIssue(&is[i], pass))
-				}
-			}
-
-			if len(issues) == 0 {
-				return nil, nil
-			}
-
-			mu.Lock()
-			resIssues = append(resIssues, issues...)
-			mu.Unlock()
-
-			return nil, nil
-		}
-	}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
-		return resIssues
-	}).WithLoadMode(goanalysis.LoadModeSyntax)
+		cfg,
+	).WithLoadMode(goanalysis.LoadModeSyntax)
 }
diff --git a/pkg/golinters/gofmt_common.go b/pkg/golinters/gofmt_common.go
index 39e8092e9735..42dc1768bc60 100644
--- a/pkg/golinters/gofmt_common.go
+++ b/pkg/golinters/gofmt_common.go
@@ -226,16 +226,14 @@ func getErrorTextForLinter(lintCtx *linter.Context, linterName string) string {
 			text += " with -local " + lintCtx.Settings().Goimports.LocalPrefixes
 		}
 	case gciName:
-		text = "File is not `gci`-ed"
-		localPrefixes := lintCtx.Settings().Gci.LocalPrefixes
-		goimportsFlag := lintCtx.Settings().Goimports.LocalPrefixes
-		if localPrefixes == "" && goimportsFlag != "" {
-			localPrefixes = goimportsFlag
+		optionsText := []string{}
+		if lintCtx.Settings().Gci.NoInlineComments {
+			optionsText = append(optionsText, "NoInlineComments")
 		}
-
-		if localPrefixes != "" {
-			text += " with -local " + localPrefixes
+		if lintCtx.Settings().Gci.NoPrefixComments {
+			optionsText = append(optionsText, "NoPrefixComments")
 		}
+		text = fmt.Sprintf("File does not conform to the import format configured for `Gci`(%s)", strings.Join(optionsText, ","))
 	}
 	return text
 }
diff --git a/pkg/lint/lintersdb/manager.go b/pkg/lint/lintersdb/manager.go
index cf6a0f8d4b6f..b72049ba7881 100644
--- a/pkg/lint/lintersdb/manager.go
+++ b/pkg/lint/lintersdb/manager.go
@@ -107,6 +107,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
 	var errorlintCfg *config.ErrorLintSettings
 	var exhaustiveCfg *config.ExhaustiveSettings
 	var exhaustiveStructCfg *config.ExhaustiveStructSettings
+	var gciCfg *config.GciSettings
 	var goModDirectivesCfg *config.GoModDirectivesSettings
 	var goMndCfg *config.GoMndSettings
 	var gosecCfg *config.GoSecSettings
@@ -139,6 +140,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
 		errorlintCfg = &m.cfg.LintersSettings.ErrorLint
 		exhaustiveCfg = &m.cfg.LintersSettings.Exhaustive
 		exhaustiveStructCfg = &m.cfg.LintersSettings.ExhaustiveStruct
+		gciCfg = &m.cfg.LintersSettings.Gci
 		goModDirectivesCfg = &m.cfg.LintersSettings.GoModDirectives
 		goMndCfg = &m.cfg.LintersSettings.Gomnd
 		gosecCfg = &m.cfg.LintersSettings.Gosec
@@ -292,7 +294,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
 			WithPresets(linter.PresetComplexity).
 			WithURL("https://github.com/ultraware/funlen"),
 
-		linter.NewConfig(golinters.NewGci()).
+		linter.NewConfig(golinters.NewGci(gciCfg)).
 			WithSince("v1.30.0").
 			WithPresets(linter.PresetFormatting, linter.PresetImport).
 			WithAutoFix().
diff --git a/test/linters_test.go b/test/linters_test.go
index bb19e212a52c..a80baa6a511c 100644
--- a/test/linters_test.go
+++ b/test/linters_test.go
@@ -92,11 +92,11 @@ func TestGciLocal(t *testing.T) {
 	rc := extractRunContextFromComments(t, sourcePath)
 	args = append(args, rc.args...)
 
-	cfg, err := yaml.Marshal(rc.config)
+	cfg, err := os.ReadFile(rc.configPath)
 	require.NoError(t, err)
 
 	testshared.NewLintRunner(t).RunWithYamlConfig(string(cfg), args...).
-		ExpectHasIssue("testdata/gci/gci.go:7: File is not `gci`-ed")
+		ExpectHasIssue("testdata/gci/gci.go:9:1: Expected '\\n', Found '\\t'")
 }
 
 func TestMultipleOutputs(t *testing.T) {
@@ -108,11 +108,11 @@ func TestMultipleOutputs(t *testing.T) {
 	rc := extractRunContextFromComments(t, sourcePath)
 	args = append(args, rc.args...)
 
-	cfg, err := yaml.Marshal(rc.config)
+	cfg, err := os.ReadFile(rc.configPath)
 	require.NoError(t, err)
 
 	testshared.NewLintRunner(t).RunWithYamlConfig(string(cfg), args...).
-		ExpectHasIssue("testdata/gci/gci.go:7: File is not `gci`-ed").
+		ExpectHasIssue("testdata/gci/gci.go:9:1: Expected '\\n', Found '\\t'").
 		ExpectOutputContains(`"Issues":[`)
 }
 
@@ -125,11 +125,11 @@ func TestStderrOutput(t *testing.T) {
 	rc := extractRunContextFromComments(t, sourcePath)
 	args = append(args, rc.args...)
 
-	cfg, err := yaml.Marshal(rc.config)
+	cfg, err := os.ReadFile(rc.configPath)
 	require.NoError(t, err)
 
 	testshared.NewLintRunner(t).RunWithYamlConfig(string(cfg), args...).
-		ExpectHasIssue("testdata/gci/gci.go:7: File is not `gci`-ed").
+		ExpectHasIssue("testdata/gci/gci.go:9:1: Expected '\\n', Found '\\t'").
 		ExpectOutputContains(`"Issues":[`)
 }
 
@@ -145,11 +145,11 @@ func TestFileOutput(t *testing.T) {
 	rc := extractRunContextFromComments(t, sourcePath)
 	args = append(args, rc.args...)
 
-	cfg, err := yaml.Marshal(rc.config)
+	cfg, err := os.ReadFile(rc.configPath)
 	require.NoError(t, err)
 
 	testshared.NewLintRunner(t).RunWithYamlConfig(string(cfg), args...).
-		ExpectHasIssue("testdata/gci/gci.go:7: File is not `gci`-ed").
+		ExpectHasIssue("testdata/gci/gci.go:9:1: Expected '\\n', Found '\\t'").
 		ExpectOutputNotContains(`"Issues":[`)
 
 	b, err := os.ReadFile(resultPath)
diff --git a/test/testdata/configs/gci.yml b/test/testdata/configs/gci.yml
new file mode 100644
index 000000000000..71f36c4c9e83
--- /dev/null
+++ b/test/testdata/configs/gci.yml
@@ -0,0 +1,6 @@
+linters-settings:
+  gci:
+    sections:
+      - Standard
+      - Prefix(github.com/golangci/golangci-lint)
+      - Default
\ No newline at end of file
diff --git a/test/testdata/gci.go b/test/testdata/gci.go
index 4cda6e6156da..8f4190a20fe2 100644
--- a/test/testdata/gci.go
+++ b/test/testdata/gci.go
@@ -1,10 +1,12 @@
 //args: -Egci
+//config_path: testdata/configs/gci.yml
 package testdata
 
 import (
 	"fmt"
 
 	"github.com/golangci/golangci-lint/pkg/config"
+
 	"github.com/pkg/errors"
 )
 
diff --git a/test/testdata/gci/gci.go b/test/testdata/gci/gci.go
index a3ab571e852b..2875032513e1 100644
--- a/test/testdata/gci/gci.go
+++ b/test/testdata/gci/gci.go
@@ -1,12 +1,11 @@
 //args: -Egci
-//config: linters-settings.gci.local-prefixes=github.com/golangci/golangci-lint
+//config_path: testdata/configs/gci.yml
 package gci
 
 import (
 	"fmt"
 
 	"github.com/golangci/golangci-lint/pkg/config"
-
 	"github.com/pkg/errors"
 )
 

From deb352c60dce0b9b9f8f6c43eaee06d4df1d13b6 Mon Sep 17 00:00:00 2001
From: Norman Gehrsitz <norman.gehrsitz@gmx.de>
Date: Mon, 31 Jan 2022 19:21:09 +0100
Subject: [PATCH 2/5] Run gci as part of the CI

Signed-off-by: Norman Gehrsitz <norman.gehrsitz@gmx.de>
---
 .golangci.yml            | 6 ++++++
 pkg/golinters/ireturn.go | 6 +++---
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/.golangci.yml b/.golangci.yml
index a17d2b119bad..e7a5e05d41e6 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -12,6 +12,11 @@ linters-settings:
   funlen:
     lines: 100
     statements: 50
+  gci:
+    sections:
+      - Standard
+      - Default
+      - Prefix(github.com/golangci/golangci-lint)
   goconst:
     min-len: 2
     min-occurrences: 3
@@ -71,6 +76,7 @@ linters:
     - errcheck
     - exportloopref
     - funlen
+    - gci
     - gochecknoinits
     - goconst
     - gocritic
diff --git a/pkg/golinters/ireturn.go b/pkg/golinters/ireturn.go
index 3b5df66dae81..f2d4aec92fef 100644
--- a/pkg/golinters/ireturn.go
+++ b/pkg/golinters/ireturn.go
@@ -3,11 +3,11 @@ package golinters
 import (
 	"strings"
 
-	"github.com/golangci/golangci-lint/pkg/config"
-	"github.com/golangci/golangci-lint/pkg/golinters/goanalysis"
-
 	"github.com/butuzov/ireturn/analyzer"
 	"golang.org/x/tools/go/analysis"
+
+	"github.com/golangci/golangci-lint/pkg/config"
+	"github.com/golangci/golangci-lint/pkg/golinters/goanalysis"
 )
 
 func NewIreturn(settings *config.IreturnSettings) *goanalysis.Linter {

From d3a4e126e8be5c1f97efa515a4d5ce6cc2e299b2 Mon Sep 17 00:00:00 2001
From: Fernandez Ludovic <ldez@users.noreply.github.com>
Date: Mon, 31 Jan 2022 23:11:05 +0100
Subject: [PATCH 3/5] first review

---
 .golangci.example.yml          | 31 ++++++++++++++++++++++---------
 .golangci.yml                  |  6 ------
 pkg/config/linters_settings.go |  5 +++++
 pkg/golinters/gci.go           | 30 +++++++++++++++++++-----------
 pkg/golinters/gofmt_common.go  |  9 ---------
 5 files changed, 46 insertions(+), 35 deletions(-)

diff --git a/.golangci.example.yml b/.golangci.example.yml
index 15022f2ec31d..48be8fe4f374 100644
--- a/.golangci.example.yml
+++ b/.golangci.example.yml
@@ -295,18 +295,31 @@ linters-settings:
     statements: -1
 
   gci:
-    # Checks that no inline Comments are present
-    no-inlineComments: false
-    # Checks that no prefix Comments(comment lines above an import) are present
-    no-prefixComments: false
+    # DEPRECATED: use `sections` and `prefix(github.com/org/project)` instead.
+    local-prefixes: github.com/org/project
+
+    # Checks that no inline Comments are present.
+    # Default: false
+    no-inlineComments: true
+
+    # Checks that no prefix Comments(comment lines above an import) are present.
+    # Default: false
+    no-prefixComments: true
+
     # Section configuration to compare against.
-    # Run gci print -h for a detailed explanation
+    # Section names are case-insensitive and may contain parameters in ().
+    # Default: ["standard", "default"]
     sections:
-      - Standard
-      - Default
-    # Separators that should be present between sections
+      - standard                       # Captures all standard packages if they do not match another section.
+      - default                        # Contains all imports that could not be matched to another section type.
+      - comment(your text here)        # Prints the specified indented comment.
+      - newLine                        # Prints an empty line
+      - prefix(github.com/org/project) # Groups all imports with the specified Prefix.
+
+    # Separators that should be present between sections.
+    # Default: ["newLine"]
     sectionSeparators:
-      - Newline
+      - newLine
 
   gocognit:
     # Minimal code complexity to report
diff --git a/.golangci.yml b/.golangci.yml
index e7a5e05d41e6..a17d2b119bad 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -12,11 +12,6 @@ linters-settings:
   funlen:
     lines: 100
     statements: 50
-  gci:
-    sections:
-      - Standard
-      - Default
-      - Prefix(github.com/golangci/golangci-lint)
   goconst:
     min-len: 2
     min-occurrences: 3
@@ -76,7 +71,6 @@ linters:
     - errcheck
     - exportloopref
     - funlen
-    - gci
     - gochecknoinits
     - goconst
     - gocritic
diff --git a/pkg/config/linters_settings.go b/pkg/config/linters_settings.go
index e538142a514d..da7237da66d6 100644
--- a/pkg/config/linters_settings.go
+++ b/pkg/config/linters_settings.go
@@ -26,6 +26,10 @@ var defaultLintersSettings = LintersSettings{
 	Forbidigo: ForbidigoSettings{
 		ExcludeGodocExamples: true,
 	},
+	Gci: GciSettings{
+		Sections:         []string{"default", "standard"},
+		SectionSeparator: []string{"newline"},
+	},
 	Gocognit: GocognitSettings{
 		MinComplexity: 30,
 	},
@@ -253,6 +257,7 @@ type FunlenSettings struct {
 }
 
 type GciSettings struct {
+	LocalPrefixes    string   `mapstructure:"local-prefixes"` // Deprecated
 	NoInlineComments bool     `mapstructure:"no-inlineComments"`
 	NoPrefixComments bool     `mapstructure:"no-prefixComments"`
 	Sections         []string `mapstructure:"sections"`
diff --git a/pkg/golinters/gci.go b/pkg/golinters/gci.go
index a54502af8624..07b4c3d68ee1 100644
--- a/pkg/golinters/gci.go
+++ b/pkg/golinters/gci.go
@@ -1,6 +1,7 @@
 package golinters
 
 import (
+	"fmt"
 	"strings"
 
 	gciAnalyzer "github.com/daixiang0/gci/pkg/analyzer"
@@ -13,23 +14,30 @@ import (
 const gciName = "gci"
 
 func NewGci(settings *config.GciSettings) *goanalysis.Linter {
-	analyzer := gciAnalyzer.Analyzer
-	var cfg map[string]map[string]interface{}
+	var linterCfg map[string]map[string]interface{}
+
 	if settings != nil {
-		cfg = map[string]map[string]interface{}{
-			analyzer.Name: {
-				gciAnalyzer.NoInlineCommentsFlag:  settings.NoInlineComments,
-				gciAnalyzer.NoPrefixCommentsFlag:  settings.NoPrefixComments,
-				gciAnalyzer.SectionsFlag:          strings.Join(settings.Sections, gciAnalyzer.SectionDelimiter),
-				gciAnalyzer.SectionSeparatorsFlag: strings.Join(settings.SectionSeparator, gciAnalyzer.SectionDelimiter),
-			},
+		cfg := map[string]interface{}{
+			gciAnalyzer.NoInlineCommentsFlag:  settings.NoInlineComments,
+			gciAnalyzer.NoPrefixCommentsFlag:  settings.NoPrefixComments,
+			gciAnalyzer.SectionsFlag:          strings.Join(settings.Sections, gciAnalyzer.SectionDelimiter),
+			gciAnalyzer.SectionSeparatorsFlag: strings.Join(settings.SectionSeparator, gciAnalyzer.SectionDelimiter),
+		}
+
+		if settings.LocalPrefixes != "" {
+			prefix := []string{"Standard", "Default", fmt.Sprintf("Prefix(%s)", settings.LocalPrefixes)}
+			cfg[gciAnalyzer.SectionsFlag] = strings.Join(prefix, gciAnalyzer.SectionDelimiter)
+		}
+
+		linterCfg = map[string]map[string]interface{}{
+			gciAnalyzer.Analyzer.Name: cfg,
 		}
 	}
 
 	return goanalysis.NewLinter(
 		gciName,
 		"Gci controls golang package import order and makes it always deterministic.",
-		[]*analysis.Analyzer{analyzer},
-		cfg,
+		[]*analysis.Analyzer{gciAnalyzer.Analyzer},
+		linterCfg,
 	).WithLoadMode(goanalysis.LoadModeSyntax)
 }
diff --git a/pkg/golinters/gofmt_common.go b/pkg/golinters/gofmt_common.go
index 42dc1768bc60..4f63e7bed853 100644
--- a/pkg/golinters/gofmt_common.go
+++ b/pkg/golinters/gofmt_common.go
@@ -225,15 +225,6 @@ func getErrorTextForLinter(lintCtx *linter.Context, linterName string) string {
 		if lintCtx.Settings().Goimports.LocalPrefixes != "" {
 			text += " with -local " + lintCtx.Settings().Goimports.LocalPrefixes
 		}
-	case gciName:
-		optionsText := []string{}
-		if lintCtx.Settings().Gci.NoInlineComments {
-			optionsText = append(optionsText, "NoInlineComments")
-		}
-		if lintCtx.Settings().Gci.NoPrefixComments {
-			optionsText = append(optionsText, "NoPrefixComments")
-		}
-		text = fmt.Sprintf("File does not conform to the import format configured for `Gci`(%s)", strings.Join(optionsText, ","))
 	}
 	return text
 }

From 770506b1da848bf53d6951f2e2cd6afbefef8ffb Mon Sep 17 00:00:00 2001
From: Fernandez Ludovic <ldez@users.noreply.github.com>
Date: Mon, 31 Jan 2022 23:16:28 +0100
Subject: [PATCH 4/5] review

---
 pkg/golinters/gci.go          | 18 +++++++++---------
 test/testdata/configs/gci.yml |  6 +++---
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/pkg/golinters/gci.go b/pkg/golinters/gci.go
index 07b4c3d68ee1..75c9e1eba166 100644
--- a/pkg/golinters/gci.go
+++ b/pkg/golinters/gci.go
@@ -4,7 +4,7 @@ import (
 	"fmt"
 	"strings"
 
-	gciAnalyzer "github.com/daixiang0/gci/pkg/analyzer"
+	gci "github.com/daixiang0/gci/pkg/analyzer"
 	"golang.org/x/tools/go/analysis"
 
 	"github.com/golangci/golangci-lint/pkg/config"
@@ -18,26 +18,26 @@ func NewGci(settings *config.GciSettings) *goanalysis.Linter {
 
 	if settings != nil {
 		cfg := map[string]interface{}{
-			gciAnalyzer.NoInlineCommentsFlag:  settings.NoInlineComments,
-			gciAnalyzer.NoPrefixCommentsFlag:  settings.NoPrefixComments,
-			gciAnalyzer.SectionsFlag:          strings.Join(settings.Sections, gciAnalyzer.SectionDelimiter),
-			gciAnalyzer.SectionSeparatorsFlag: strings.Join(settings.SectionSeparator, gciAnalyzer.SectionDelimiter),
+			gci.NoInlineCommentsFlag:  settings.NoInlineComments,
+			gci.NoPrefixCommentsFlag:  settings.NoPrefixComments,
+			gci.SectionsFlag:          strings.Join(settings.Sections, gci.SectionDelimiter),
+			gci.SectionSeparatorsFlag: strings.Join(settings.SectionSeparator, gci.SectionDelimiter),
 		}
 
 		if settings.LocalPrefixes != "" {
-			prefix := []string{"Standard", "Default", fmt.Sprintf("Prefix(%s)", settings.LocalPrefixes)}
-			cfg[gciAnalyzer.SectionsFlag] = strings.Join(prefix, gciAnalyzer.SectionDelimiter)
+			prefix := []string{"standard", "default", fmt.Sprintf("prefix(%s)", settings.LocalPrefixes)}
+			cfg[gci.SectionsFlag] = strings.Join(prefix, gci.SectionDelimiter)
 		}
 
 		linterCfg = map[string]map[string]interface{}{
-			gciAnalyzer.Analyzer.Name: cfg,
+			gci.Analyzer.Name: cfg,
 		}
 	}
 
 	return goanalysis.NewLinter(
 		gciName,
 		"Gci controls golang package import order and makes it always deterministic.",
-		[]*analysis.Analyzer{gciAnalyzer.Analyzer},
+		[]*analysis.Analyzer{gci.Analyzer},
 		linterCfg,
 	).WithLoadMode(goanalysis.LoadModeSyntax)
 }
diff --git a/test/testdata/configs/gci.yml b/test/testdata/configs/gci.yml
index 71f36c4c9e83..487e26383697 100644
--- a/test/testdata/configs/gci.yml
+++ b/test/testdata/configs/gci.yml
@@ -1,6 +1,6 @@
 linters-settings:
   gci:
     sections:
-      - Standard
-      - Prefix(github.com/golangci/golangci-lint)
-      - Default
\ No newline at end of file
+      - standard
+      - prefix(github.com/golangci/golangci-lint)
+      - default

From d7a5f3b1849c6585063746be98a5ea1992b3de18 Mon Sep 17 00:00:00 2001
From: Fernandez Ludovic <ldez@users.noreply.github.com>
Date: Wed, 2 Feb 2022 00:23:50 +0100
Subject: [PATCH 5/5] review: add log for local-prefixes

---
 pkg/golinters/gci.go | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/pkg/golinters/gci.go b/pkg/golinters/gci.go
index 75c9e1eba166..c0c606a7b89d 100644
--- a/pkg/golinters/gci.go
+++ b/pkg/golinters/gci.go
@@ -9,6 +9,7 @@ import (
 
 	"github.com/golangci/golangci-lint/pkg/config"
 	"github.com/golangci/golangci-lint/pkg/golinters/goanalysis"
+	"github.com/golangci/golangci-lint/pkg/lint/linter"
 )
 
 const gciName = "gci"
@@ -39,5 +40,9 @@ func NewGci(settings *config.GciSettings) *goanalysis.Linter {
 		"Gci controls golang package import order and makes it always deterministic.",
 		[]*analysis.Analyzer{gci.Analyzer},
 		linterCfg,
-	).WithLoadMode(goanalysis.LoadModeSyntax)
+	).WithContextSetter(func(lintCtx *linter.Context) {
+		if settings.LocalPrefixes != "" {
+			lintCtx.Log.Warnf("gci: `local-prefixes` is deprecated, use `sections` and `prefix(%s)` instead.", settings.LocalPrefixes)
+		}
+	}).WithLoadMode(goanalysis.LoadModeSyntax)
 }