@@ -6,16 +6,23 @@ import { getIncludeParams, LANGUAGE_ID } from '../lib/helper'
6
6
7
7
import * as vscode from 'vscode'
8
8
import { LoggingService } from '../services/logging-service'
9
+ import { Config } from '../services/config'
10
+ import { EnvironmentVariables } from '../services/utils'
11
+
12
+ const ERROR_REGEX : RegExp =
13
+ / ^ ( [ a - z A - Z ] : \\ ) * ( [ ^ : ] * ) : ( [ 0 - 9 ] + ) : ( [ 0 - 9 ] + ) : \s + ( .* ) \s + .* ?\s + ( E r r o r | W a r n i n g | F a t a l E r r o r ) : \s ( .* ) $ / gm
14
+
15
+ const knownModNames = [ 'mpi' ]
9
16
10
17
export default class FortranLintingProvider {
11
- constructor ( private loggingService : LoggingService ) { }
18
+ constructor (
19
+ private loggingService : LoggingService ,
20
+ private _config : Config
21
+ ) { }
12
22
13
23
private diagnosticCollection : vscode . DiagnosticCollection
14
24
15
- private doModernFortranLint ( textDocument : vscode . TextDocument ) {
16
- const errorRegex : RegExp =
17
- / ^ ( [ a - z A - Z ] : \\ ) * ( [ ^ : ] * ) : ( [ 0 - 9 ] + ) : ( [ 0 - 9 ] + ) : \s + ( .* ) \s + .* ?\s + ( E r r o r | W a r n i n g | F a t a l E r r o r ) : \s ( .* ) $ / gm
18
-
25
+ private async doModernFortranLint ( textDocument : vscode . TextDocument ) {
19
26
if (
20
27
textDocument . languageId !== LANGUAGE_ID ||
21
28
textDocument . uri . scheme !== 'file'
@@ -24,9 +31,9 @@ export default class FortranLintingProvider {
24
31
}
25
32
26
33
let decoded = ''
27
- let diagnostics : vscode . Diagnostic [ ] = [ ]
28
- let command = this . getGfortranPath ( )
29
- let argList = this . constructArgumentList ( textDocument )
34
+
35
+ let command = await this . getGfortranPath ( )
36
+ let argList = await this . constructArgumentList ( textDocument )
30
37
31
38
let filePath = path . parse ( textDocument . fileName ) . dir
32
39
@@ -37,70 +44,82 @@ export default class FortranLintingProvider {
37
44
*
38
45
* see also: https://gcc.gnu.org/onlinedocs/gcc/Environment-Variables.html
39
46
*/
40
- const env = process . env
41
- env . LC_ALL = 'C'
47
+ const env : EnvironmentVariables = { ...process . env , LC_ALL : 'C' }
42
48
if ( process . platform === 'win32' ) {
43
49
// Windows needs to know the path of other tools
44
50
if ( ! env . Path . includes ( path . dirname ( command ) ) ) {
45
51
env . Path = `${ path . dirname ( command ) } ${ path . delimiter } ${ env . Path } `
46
52
}
47
53
}
48
- let childProcess = cp . spawn ( command , argList , { cwd : filePath , env : env } )
54
+ this . loggingService . logInfo (
55
+ `executing linter command ${ command } ${ argList . join ( ' ' ) } `
56
+ )
57
+ let gfortran = cp . spawn ( command , argList , { cwd : filePath , env } )
49
58
50
- if ( childProcess . pid ) {
51
- childProcess . stdout . on ( 'data' , ( data : Buffer ) => {
59
+ if ( gfortran && gfortran . pid ) {
60
+ gfortran ! . stdout ! . on ( 'data' , ( data : Buffer ) => {
52
61
decoded += data
53
62
} )
54
- childProcess . stderr . on ( 'data' , ( data ) => {
63
+ gfortran ! . stderr ! . on ( 'data' , ( data ) => {
55
64
decoded += data
56
65
} )
57
- childProcess . stderr . on ( 'end' , ( ) => {
58
- let matchesArray : string [ ]
59
- while ( ( matchesArray = errorRegex . exec ( decoded ) ) !== null ) {
60
- let elements : string [ ] = matchesArray . slice ( 1 ) // get captured expressions
61
- let startLine = parseInt ( elements [ 2 ] )
62
- let startColumn = parseInt ( elements [ 3 ] )
63
- let type = elements [ 5 ] // error or warning
64
- let severity =
65
- type . toLowerCase ( ) === 'warning'
66
- ? vscode . DiagnosticSeverity . Warning
67
- : vscode . DiagnosticSeverity . Error
68
- let message = elements [ 6 ]
69
- let range = new vscode . Range (
70
- new vscode . Position ( startLine - 1 , startColumn ) ,
71
- new vscode . Position ( startLine - 1 , startColumn )
72
- )
73
- let diagnostic = new vscode . Diagnostic ( range , message , severity )
74
- diagnostics . push ( diagnostic )
75
- }
76
-
77
- this . diagnosticCollection . set ( textDocument . uri , diagnostics )
66
+ gfortran ! . stderr . on ( 'end' , ( ) => {
67
+ this . reportErrors ( decoded , textDocument )
78
68
} )
79
- childProcess . stdout . on ( 'close' , ( code ) => {
69
+ gfortran . stdout . on ( 'close' , ( code ) => {
80
70
console . log ( `child process exited with code ${ code } ` )
81
71
} )
82
72
} else {
83
- childProcess . on ( 'error' , ( err : any ) => {
73
+ gfortran . on ( 'error' , ( err : any ) => {
84
74
if ( err . code === 'ENOENT' ) {
85
75
vscode . window . showErrorMessage (
86
- "gfortran can't found on path, update your settings with a proper path or disable the linter."
76
+ "gfortran executable can't be found at the provided path, update your settings with a proper path or disable the linter."
87
77
)
88
78
}
89
79
} )
90
80
}
91
81
}
92
82
93
- private constructArgumentList ( textDocument : vscode . TextDocument ) : string [ ] {
94
- let options = vscode . workspace . rootPath
95
- ? { cwd : vscode . workspace . rootPath }
96
- : undefined
83
+ reportErrors ( errors : string , textDocument : vscode . TextDocument ) {
84
+ let diagnostics : vscode . Diagnostic [ ] = [ ]
85
+ let matchesArray : string [ ]
86
+ while ( ( matchesArray = ERROR_REGEX . exec ( errors ) ) !== null ) {
87
+ let elements : string [ ] = matchesArray . slice ( 1 ) // get captured expressions
88
+ let startLine = parseInt ( elements [ 2 ] )
89
+ let startColumn = parseInt ( elements [ 3 ] )
90
+ let type = elements [ 5 ] // error or warning
91
+ let severity =
92
+ type . toLowerCase ( ) === 'warning'
93
+ ? vscode . DiagnosticSeverity . Warning
94
+ : vscode . DiagnosticSeverity . Error
95
+ let message = elements [ 6 ]
96
+ const [ isModError , modName ] = isModuleMissingErrorMessage ( message )
97
+ // skip error from known mod names
98
+ if ( isModError && knownModNames . includes ( modName ) ) {
99
+ continue
100
+ }
101
+ let range = new vscode . Range (
102
+ new vscode . Position ( startLine - 1 , startColumn ) ,
103
+ new vscode . Position ( startLine - 1 , startColumn )
104
+ )
105
+
106
+ let diagnostic = new vscode . Diagnostic ( range , message , severity )
107
+ diagnostics . push ( diagnostic )
108
+ }
109
+
110
+ this . diagnosticCollection . set ( textDocument . uri , diagnostics )
111
+ }
112
+
113
+ private async constructArgumentList (
114
+ textDocument : vscode . TextDocument
115
+ ) : Promise < string [ ] > {
97
116
let args = [
98
117
'-fsyntax-only' ,
99
118
'-cpp' ,
100
119
'-fdiagnostics-show-option' ,
101
- ...this . getLinterExtraArgs ( ) ,
120
+ ...( await this . getLinterExtraArgs ( ) ) ,
102
121
]
103
- let includePaths = this . getIncludePaths ( )
122
+ let includePaths = await this . getIncludePaths ( )
104
123
105
124
let extensionIndex = textDocument . fileName . lastIndexOf ( '.' )
106
125
let fileNameWithoutExtension = textDocument . fileName . substring (
@@ -164,21 +183,34 @@ export default class FortranLintingProvider {
164
183
this . command . dispose ( )
165
184
}
166
185
167
- private getIncludePaths ( ) : string [ ] {
168
- let config = vscode . workspace . getConfiguration ( 'fortran' )
169
- let includePaths : string [ ] = config . get ( 'includePaths' , [ ] )
170
-
186
+ private async getIncludePaths ( ) : Promise < string [ ] > {
187
+ let includePaths : string [ ] = await this . _config . get ( 'includePaths' , [ ] )
188
+ this . loggingService . logInfo ( `using include paths "${ includePaths } "` )
171
189
return includePaths
172
190
}
173
- private getGfortranPath ( ) : string {
174
- let config = vscode . workspace . getConfiguration ( 'fortran' )
175
- const gfortranPath = config . get ( 'gfortranExecutable' , 'gfortran' )
176
- this . loggingService . logInfo ( `using gfortran executable: ${ gfortranPath } ` )
191
+
192
+ private async getGfortranPath ( ) : Promise < string > {
193
+ const gfortranPath = await this . _config . get (
194
+ 'gfortranExecutable' ,
195
+ 'gfortran'
196
+ )
197
+ this . loggingService . logInfo ( `using gfortran executable: "${ gfortranPath } "` )
177
198
return gfortranPath
178
199
}
179
200
180
- private getLinterExtraArgs ( ) : string [ ] {
181
- let config = vscode . workspace . getConfiguration ( 'fortran' )
182
- return config . get ( 'linterExtraArgs' , [ '-Wall' ] )
201
+ private getLinterExtraArgs ( ) : Promise < string [ ] > {
202
+ return this . _config . get ( 'linterExtraArgs' , [ '-Wall' ] )
203
+ }
204
+ }
205
+
206
+ function isModuleMissingErrorMessage (
207
+ message : string
208
+ ) : [ boolean , string | null ] {
209
+ const result = / ^ C a n n o t o p e n m o d u l e f i l e ' ( \w + ) .m o d ' f o r r e a d i n g / . exec (
210
+ message
211
+ )
212
+ if ( result ) {
213
+ return [ true , result [ 1 ] ]
183
214
}
215
+ return [ false , null ]
184
216
}
0 commit comments