1+ import { normalize , resolve } from "path" ;
12import { Ext } from "../extension" ;
23import {
34 parsePHPStanAnalyseResult ,
@@ -8,6 +9,7 @@ import showOutput from "./showOutput";
89import stopAnalyse from "./stopAnalyse" ;
910import { spawn } from "child_process" ;
1011import { Diagnostic , DiagnosticSeverity , Range , Uri } from "vscode" ;
12+ import { getFileLines } from "../utils/fs" ;
1113
1214function setStatusBarProgress ( ext : Ext , progress ?: number ) {
1315 let text = "$(sync~spin) PHPStan analysing..." ;
@@ -25,6 +27,7 @@ async function refreshDiagnostics(ext: Ext, result: PHPStanAnalyseResult) {
2527 for ( const error of result . errors ) {
2628 const range = new Range ( 0 , 0 , 0 , 0 ) ;
2729 const diagnostic = new Diagnostic ( range , error , DiagnosticSeverity . Error ) ;
30+ diagnostic . source = ext . options . name ;
2831 globalDiagnostics . push ( diagnostic ) ;
2932 }
3033
@@ -38,26 +41,62 @@ async function refreshDiagnostics(ext: Ext, result: PHPStanAnalyseResult) {
3841 // https://github.com/phpstan/phpstan-src/blob/6d228a53/src/Analyser/MutatingScope.php#L289
3942 const contextRegex = / \( i n c o n t e x t o f .+ \) $ / ;
4043
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+
4279 const pathItem = result . files [ path ] ;
4380 const diagnostics : Diagnostic [ ] = [ ] ;
4481 for ( const messageItem of pathItem . messages ) {
4582 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 ) ;
4789 const diagnostic = new Diagnostic (
4890 range ,
4991 messageItem . message ,
5092 DiagnosticSeverity . Error
5193 ) ;
94+ diagnostic . source = ext . options . name ;
5295
5396 diagnostics . push ( diagnostic ) ;
5497 }
5598
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 ) ;
61100 }
62101}
63102
0 commit comments