@@ -10,44 +10,117 @@ import (
10
10
"github.com/sourcegraph/jsonrpc2"
11
11
)
12
12
13
- func NewHandler (logger logger , noLinterName bool ) jsonrpc2.Handler {
13
+ func NewHandler (logger logger , noLinterName bool , fromConfigDir bool ) jsonrpc2.Handler {
14
14
handler := & langHandler {
15
- logger : logger ,
16
- request : make (chan DocumentURI ),
17
- noLinterName : noLinterName ,
15
+ logger : logger ,
16
+ request : make (chan DocumentURI ),
17
+ noLinterName : noLinterName ,
18
+ fromConfigDir : fromConfigDir ,
18
19
}
19
20
go handler .linter ()
20
21
21
22
return jsonrpc2 .HandlerWithError (handler .handle )
22
23
}
23
24
24
25
type langHandler struct {
25
- logger logger
26
- conn * jsonrpc2.Conn
27
- request chan DocumentURI
28
- command []string
29
- noLinterName bool
26
+ logger logger
27
+ conn * jsonrpc2.Conn
28
+ request chan DocumentURI
29
+ command []string
30
+ noLinterName bool
31
+ fromConfigDir bool
30
32
31
33
rootURI string
32
34
}
33
35
34
- func (h * langHandler ) lint ( uri DocumentURI ) ([] Diagnostic , error ) {
35
- diagnostics := make ([] Diagnostic , 0 )
36
+ func (h * langHandler ) findConfigDirectory ( file string ) (string , error ) {
37
+ dir , _ := filepath . Split ( file )
36
38
37
- path := uriToPath (string (uri ))
38
- dir , file := filepath .Split (path )
39
+ // Maybe this should be configurable too?
40
+ findConfigCmd := exec .Command (h .command [0 ], "config" , "path" )
41
+ findConfigCmd .Dir = dir
42
+
43
+ configFileBytes , err := findConfigCmd .Output ()
44
+ if err != nil {
45
+ return "" , err
46
+ }
47
+
48
+ configDirRel , _ := filepath .Split (string (configFileBytes ))
49
+
50
+ // configFileBytes is relative to dir, we need to make it absolute
51
+ //
52
+ // This assumes that `file` is absolute
53
+ return filepath .Join (dir , configDirRel ), nil
54
+ }
39
55
56
+ func (h * langHandler ) runLinter (dir string ) (GolangCILintResult , error ) {
40
57
//nolint:gosec
41
58
cmd := exec .Command (h .command [0 ], h .command [1 :]... )
42
- cmd .Dir = dir
59
+
60
+ // linter might be ran either from the directory of the file or from the directory of the config file
61
+ var checkDir string
62
+ if h .fromConfigDir {
63
+ // Relative paths in .golangci-lint.yml work, but
64
+ // - we need to find the directory with config
65
+ // - we have to adjust the paths in the result
66
+
67
+ configDir , err := h .findConfigDirectory (dir )
68
+ if err != nil {
69
+ return GolangCILintResult {}, err
70
+ }
71
+
72
+ h .logger .Printf ("Found golangci-lint config file in directory %s" , configDir )
73
+
74
+ // Find the original directory, relative to the config dir
75
+ checkDir , err = filepath .Rel (configDir , dir )
76
+ if err != nil {
77
+ return GolangCILintResult {}, err
78
+ }
79
+
80
+ // This runs the linter on the subtree. Non-config-dir option does not
81
+ // pass any packages to the command line, which is equivalent.
82
+ cmd .Args = append (cmd .Args , checkDir + "/..." )
83
+ cmd .Dir = configDir
84
+ } else {
85
+ // Relative paths in golangci-lint.yml don't work, but the paths in report are correct,
86
+ // and no additional work is needed
87
+ cmd .Dir = dir
88
+ }
43
89
44
90
b , err := cmd .Output ()
45
- if err == nil {
46
- return diagnostics , nil
91
+ if err == nil { // This expects that the golangci-lint exits with non-zero code on errors
92
+ return GolangCILintResult {} , nil
47
93
}
48
94
49
95
var result GolangCILintResult
50
96
if err := json .Unmarshal (b , & result ); err != nil {
97
+ return GolangCILintResult {}, err
98
+ }
99
+
100
+ // We need to adjust the paths in the result (see above)
101
+ if h .fromConfigDir {
102
+ var issues []Issue
103
+ for _ , issue := range result .Issues {
104
+ // Strip checkDir from the path
105
+ issue .Pos .Filename , err = filepath .Rel (checkDir , issue .Pos .Filename )
106
+ if err != nil {
107
+ return GolangCILintResult {}, err
108
+ }
109
+ issues = append (issues , issue )
110
+ }
111
+ result .Issues = issues
112
+ }
113
+
114
+ return result , nil
115
+ }
116
+
117
+ func (h * langHandler ) lint (uri DocumentURI ) ([]Diagnostic , error ) {
118
+ dir , file := filepath .Split (uriToPath (string (uri )))
119
+
120
+ diagnostics := make ([]Diagnostic , 0 )
121
+
122
+ result , err := h .runLinter (dir )
123
+ if err != nil {
51
124
return diagnostics , err
52
125
}
53
126
0 commit comments