Skip to content

Commit 8d26ccd

Browse files
committed
Implement localSourceRoot and serverSourceRoot settings
1 parent 1e04edb commit 8d26ccd

File tree

4 files changed

+75
-16
lines changed

4 files changed

+75
-16
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,18 @@ What is not supported?
4646
- Breaking on _caught_ exceptions, this is not supported by XDebug and the setting is ignored
4747
- Attach requests, there is no such thing because the lifespan of PHP scripts is short
4848

49+
Remote Host Debugging
50+
---------------------
51+
If you want to debug a running application on a remote host, you have to set the `localSourceRoot` and `serverSourceRoot` settings in your launch.json.
52+
Example:
53+
```json
54+
"serverSourceRoot": "/var/www/myproject",
55+
"localSourceRoot": "./src"
56+
```
57+
`localSourceRoot` is resolved relative to the project root (the currently opened folder in VS Code).
58+
Both paths are normalized, so you can use slashes or backslashes no matter of the OS you're running.
59+
If no `localSourceRoot` is specified, the project root is assumed.
60+
4961
FAQ
5062
---
5163

package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"dependencies": {
3636
"iconv-lite": "^0.4.13",
3737
"moment": "^2.10.6",
38+
"url-relative": "^1.0.0",
3839
"vscode-debugadapter": "^1.0.3",
3940
"vscode-debugprotocol": "^1.0.1",
4041
"xmldom": "^0.1.19"
@@ -75,6 +76,14 @@
7576
"type": "number",
7677
"description": "Port on which to listen for XDebug",
7778
"default": 9000
79+
},
80+
"serverSourceRoot": {
81+
"type": "string",
82+
"description": "The source root when debugging a remote host"
83+
},
84+
"localSourceRoot": {
85+
"type": "string",
86+
"description": "The source root on this machine that is the equivalent to the serverSourceRoot on the server. May be relative to the project root."
7887
}
7988
}
8089
}

src/phpDebug.ts

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,12 @@ import * as vscode from 'vscode-debugadapter';
22
import {DebugProtocol as VSCodeDebugProtocol} from 'vscode-debugprotocol';
33
import * as net from 'net';
44
import * as xdebug from './xdebugConnection';
5+
import urlRelative = require('url-relative');
56
import moment = require('moment');
67
import * as url from 'url';
78
import * as path from 'path';
89
import * 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 */
2512
function 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

6051
class 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
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
declare module 'url-relative' {
2+
function relative(from: string, to: string): string;
3+
export = relative;
4+
}

0 commit comments

Comments
 (0)