1
+ import { normalize , resolve } from "path" ;
1
2
import { Ext } from "../extension" ;
2
3
import {
3
4
parsePHPStanAnalyseResult ,
@@ -8,6 +9,7 @@ import showOutput from "./showOutput";
8
9
import stopAnalyse from "./stopAnalyse" ;
9
10
import { spawn } from "child_process" ;
10
11
import { Diagnostic , DiagnosticSeverity , Range , Uri } from "vscode" ;
12
+ import { getFileLines } from "../utils/fs" ;
11
13
12
14
function setStatusBarProgress ( ext : Ext , progress ?: number ) {
13
15
let text = "$(sync~spin) PHPStan analysing..." ;
@@ -25,6 +27,7 @@ async function refreshDiagnostics(ext: Ext, result: PHPStanAnalyseResult) {
25
27
for ( const error of result . errors ) {
26
28
const range = new Range ( 0 , 0 , 0 , 0 ) ;
27
29
const diagnostic = new Diagnostic ( range , error , DiagnosticSeverity . Error ) ;
30
+ diagnostic . source = ext . options . name ;
28
31
globalDiagnostics . push ( diagnostic ) ;
29
32
}
30
33
@@ -38,26 +41,62 @@ async function refreshDiagnostics(ext: Ext, result: PHPStanAnalyseResult) {
38
41
// https://github.com/phpstan/phpstan-src/blob/6d228a53/src/Analyser/MutatingScope.php#L289
39
42
const contextRegex = / \( i n c o n t e x t o f .+ \) $ / ;
40
43
41
- for ( let path in result . files ) {
44
+ const pathMaps : {
45
+ src : string ,
46
+ dest : string ,
47
+ } [ ] = [ ] ;
48
+
49
+ ext . settings . pathMappings . split ( ',' ) . map ( mapping => {
50
+ const parts = mapping . split ( ':' ) . map ( p => p . trim ( ) ) . map ( p => p . length > 0 ? p : '.' ) . map ( normalize ) ;
51
+ if ( parts . length === 2 && parts [ 0 ] && parts [ 1 ] ) {
52
+ pathMaps . push ( {
53
+ src : parts [ 0 ] + '/' ,
54
+ dest : parts [ 1 ] + '/' ,
55
+ } ) ;
56
+ }
57
+ } ) ;
58
+
59
+ ext . log ( 'Using path mappings: ' + JSON . stringify ( pathMaps ) ) ;
60
+
61
+ for ( const path in result . files ) {
62
+ let realPath = path ;
63
+
64
+ const matches = contextRegex . exec ( realPath ) ;
65
+
66
+ if ( matches ) realPath = realPath . slice ( 0 , matches . index ) ;
67
+
68
+ realPath = normalize ( realPath ) ;
69
+
70
+ for ( const pathMap of pathMaps ) {
71
+ if ( realPath . startsWith ( pathMap . src ) ) {
72
+ realPath = resolve ( ext . cwd , pathMap . dest + realPath . substring ( pathMap . src . length ) ) ;
73
+ break ;
74
+ }
75
+ }
76
+
77
+ const fileLines : string [ ] = await getFileLines ( resolve ( realPath ) ) ;
78
+
42
79
const pathItem = result . files [ path ] ;
43
80
const diagnostics : Diagnostic [ ] = [ ] ;
44
81
for ( const messageItem of pathItem . messages ) {
45
82
const line = messageItem . line ? messageItem . line - 1 : 0 ;
46
- const range = new Range ( line , 0 , line , 0 ) ;
83
+ const lineText = messageItem . line ? ( fileLines [ line ] ?? '' ) : '' ;
84
+
85
+ const startCol = Math . max ( 0 , lineText . search ( / [ ^ \s ] / g) ) ;
86
+ const endCol = Math . max ( 0 , lineText . search ( / \s * $ / g) ) ;
87
+
88
+ const range = new Range ( line , startCol , line , endCol ) ;
47
89
const diagnostic = new Diagnostic (
48
90
range ,
49
91
messageItem . message ,
50
92
DiagnosticSeverity . Error
51
93
) ;
94
+ diagnostic . source = ext . options . name ;
52
95
53
96
diagnostics . push ( diagnostic ) ;
54
97
}
55
98
56
- const matches = contextRegex . exec ( path ) ;
57
-
58
- if ( matches ) path = path . slice ( 0 , matches . index ) ;
59
-
60
- diagnostic . set ( Uri . file ( path ) , diagnostics ) ;
99
+ diagnostic . set ( Uri . file ( realPath ) , diagnostics ) ;
61
100
}
62
101
}
63
102
0 commit comments