@@ -2,25 +2,12 @@ import * as vscode from 'vscode-debugadapter';
22import { DebugProtocol as VSCodeDebugProtocol } from 'vscode-debugprotocol' ;
33import * as net from 'net' ;
44import * as xdebug from './xdebugConnection' ;
5+ import urlRelative = require( 'url-relative' ) ;
56import moment = require( 'moment' ) ;
67import * as url from 'url' ;
78import * as path from 'path' ;
89import * as util from 'util' ;
910
10- /** converts a file path to file:// URI */
11- function path2uri ( str : string ) : string {
12- var pathName = str . replace ( / \\ / g, '/' ) ;
13- if ( pathName [ 0 ] !== '/' ) {
14- pathName = '/' + pathName ;
15- }
16- return encodeURI ( 'file://' + pathName ) ;
17- }
18-
19- /** converts a file:// URI to a local file path */
20- function uri2path ( uri : string ) : string {
21- return url . parse ( uri ) . pathname . substr ( 1 ) ;
22- }
23-
2411/** formats a xdebug property value for VS Code */
2512function formatPropertyValue ( property : xdebug . BaseProperty ) : string {
2613 let displayValue : string ;
@@ -55,6 +42,10 @@ interface LaunchRequestArguments extends VSCodeDebugProtocol.LaunchRequestArgume
5542 port : number ;
5643 /** Automatically stop target after launch. If not specified, target does not stop. */
5744 stopOnEntry ?: boolean ;
45+ /** The source root on the server when doing remote debugging on a different host */
46+ serverSourceRoot ?: string ;
47+ /** The path to the source root on this machine that is the equivalent to the serverSourceRoot on the server. May be relative to the project root. */
48+ localSourceRoot ?: string ;
5849}
5950
6051class PhpDebugSession extends vscode . DebugSession {
@@ -96,6 +87,14 @@ class PhpDebugSession extends vscode.DebugSession {
9687 }
9788
9889 protected launchRequest ( response : VSCodeDebugProtocol . LaunchResponse , args : LaunchRequestArguments ) : void {
90+ if ( args . serverSourceRoot ) {
91+ // use cwd by default for localSourceRoot
92+ if ( ! args . localSourceRoot ) {
93+ args . localSourceRoot = '.' ;
94+ }
95+ // resolve localSourceRoot relative to the project root
96+ args . localSourceRoot = path . resolve ( process . cwd ( ) , args . localSourceRoot ) ;
97+ }
9998 this . _args = args ;
10099 const server = this . _server = net . createServer ( ) ;
101100 server . on ( 'connection' , ( socket : net . Socket ) => {
@@ -196,6 +195,41 @@ class PhpDebugSession extends vscode.DebugSession {
196195 }
197196 }
198197
198+ /** converts a server-side XDebug file URI to a local path for VS Code with respect to source root settings */
199+ protected convertDebuggerPathToClient ( fileUri : string ) : string {
200+ // convert the file URI to a path
201+ const serverPath = url . parse ( fileUri ) . pathname . substr ( 1 ) ;
202+ let localPath : string ;
203+ if ( this . _args . serverSourceRoot && this . _args . localSourceRoot ) {
204+ // get the part of the path that is relative to the source root
205+ const pathRelativeToSourceRoot = path . relative ( this . _args . serverSourceRoot , serverPath ) ;
206+ // resolve from the local source root
207+ localPath = path . resolve ( this . _args . localSourceRoot , pathRelativeToSourceRoot ) ;
208+ } else {
209+ localPath = path . normalize ( serverPath ) ;
210+ }
211+ return localPath ;
212+ }
213+
214+ /** converts a local path from VS Code to a server-side XDebug file URI with respect to source root settings */
215+ protected convertClientPathToDebugger ( localPath : string ) : string {
216+ let localFileUri = localPath . replace ( / \\ / g, '/' ) ;
217+ if ( localFileUri [ 0 ] !== '/' ) {
218+ localFileUri = '/' + localFileUri ;
219+ }
220+ localFileUri = encodeURI ( 'file://' + localFileUri ) ;
221+ let serverFileUri : string ;
222+ if ( this . _args . serverSourceRoot && this . _args . localSourceRoot ) {
223+ // get the part of the path that is relative to the source root
224+ const urlRelativeToSourceRoot = urlRelative ( this . _args . localSourceRoot , localPath ) ;
225+ // resolve from the server source root
226+ serverFileUri = url . resolve ( this . _args . serverSourceRoot , urlRelativeToSourceRoot ) ;
227+ } else {
228+ serverFileUri = localFileUri ;
229+ }
230+ return localFileUri ;
231+ }
232+
199233 /** Logs all requests before dispatching */
200234 protected dispatchRequest ( request : VSCodeDebugProtocol . Request ) {
201235 console . log ( `\n\n-> ${ request . command } Request` ) ;
@@ -217,7 +251,7 @@ class PhpDebugSession extends vscode.DebugSession {
217251
218252 /** This is called for each source file that has breakpoints with all the breakpoints in that file and whenever these change. */
219253 protected setBreakPointsRequest ( response : VSCodeDebugProtocol . SetBreakpointsResponse , args : VSCodeDebugProtocol . SetBreakpointsArguments ) {
220- const fileUri = path2uri ( args . source . path ) ;
254+ const fileUri = this . convertClientPathToDebugger ( args . source . path ) ;
221255 const connections = Array . from ( this . _connections . values ( ) ) ;
222256 let breakpoints : vscode . Breakpoint [ ] ;
223257 let breakpointsSetPromise : Promise < any > ;
@@ -330,7 +364,7 @@ class PhpDebugSession extends vscode.DebugSession {
330364 response . body = {
331365 stackFrames : xdebugResponse . stack . map ( stackFrame => {
332366 // XDebug paths are URIs, VS Code file paths
333- const filePath = uri2path ( stackFrame . fileUri ) ;
367+ const filePath = this . convertDebuggerPathToClient ( stackFrame . fileUri ) ;
334368 // "Name" of the source and the actual file path
335369 const source = new vscode . Source ( path . basename ( filePath ) , filePath ) ;
336370 // a new, unique ID for scopeRequests
0 commit comments