Skip to content

Commit 4714c6f

Browse files
committed
Add interprocess communication between debug adapter and extension host
1 parent 008e04d commit 4714c6f

File tree

6 files changed

+173
-2
lines changed

6 files changed

+173
-2
lines changed

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
],
2626
"license": "SEE LICENSE IN LICENSE.txt",
2727
"dependencies": {
28+
"node-ipc": "^7.0.0",
2829
"source-map": "^0.5.3",
2930
"universal-analytics": "^0.3.11",
3031
"vscode-debugadapter": "^1.7.0",

src/debug-adapter/webKitDebugAdapter.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import * as ns from '../services/NsCliService';
1515
import {ILaunchRequestArgs, IAttachRequestArgs, IDebugAdapter, ISetBreakpointsArgs, ISetBreakpointsResponseBody, IBreakpoint,
1616
IStackTraceResponseBody, IScopesResponseBody, IVariablesResponseBody, ISourceResponseBody, IThreadsResponseBody, IEvaluateResponseBody} from './WebKitAdapterInterfaces';
1717
import {AnalyticsService} from '../services/analytics/AnalyticsService';
18-
18+
import {ExtensionClient} from '../services/ipc/ExtensionClient';
1919

2020
interface IScopeVarHandle {
2121
objectId: string;
@@ -115,7 +115,8 @@ export class WebKitDebugAdapter implements IDebugAdapter {
115115
}
116116

117117
private _attach(args: IAttachRequestArgs | ILaunchRequestArgs) {
118-
AnalyticsService.getInstance().launchDebugger(args.request, args.platform, args.emulator);
118+
ExtensionClient.setAppRoot(utils.getAppRoot(args));
119+
ExtensionClient.getInstance().analyticsLaunchDebugger({ request: args.request, platform: args.platform, emulator: args.emulator });
119120
this.initDiagnosticLogging(args.request, args);
120121
this.appRoot = utils.getAppRoot(args);
121122
this.platform = args.platform;

src/main.ts

+6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as child from 'child_process';
33
import * as ns from './services/NsCliService';
44
import {ExtensionVersionInfo} from './services/ExtensionVersionInfo';
55
import {AnalyticsService} from './services/analytics/AnalyticsService';
6+
import {ExtensionServer} from './services/ipc/ExtensionServer';
67

78
function performVersionsCheck(context: vscode.ExtensionContext) {
89
// Check the state of the existing NativeScript CLI
@@ -40,6 +41,7 @@ function performVersionsCheck(context: vscode.ExtensionContext) {
4041

4142
// this method is called when the extension is activated
4243
export function activate(context: vscode.ExtensionContext) {
44+
ExtensionServer.getInstance().start();
4345
performVersionsCheck(context);
4446

4547
let runCommand = (project: ns.NSProject, options: string[]) => {
@@ -100,4 +102,8 @@ export function activate(context: vscode.ExtensionContext) {
100102

101103
context.subscriptions.push(runIosCommand);
102104
context.subscriptions.push(runAndroidCommand);
105+
}
106+
107+
export function deactivate() {
108+
ExtensionServer.getInstance().stop();
103109
}

src/services/ipc/ExtensionClient.ts

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import * as path from 'path';
2+
import * as extProtocol from './ExtensionProtocol';
3+
let ipc = require('node-ipc');
4+
5+
export class ExtensionClient {
6+
private static _appRoot: string;
7+
private static _instance: ExtensionClient;
8+
9+
private _idCounter = 0;
10+
private _pendingRequests: Object;
11+
12+
private _ipcClientInitialized: Promise<any>;
13+
14+
public static getInstance() {
15+
if (!this._instance) {
16+
this._instance = new ExtensionClient();
17+
}
18+
return this._instance;
19+
}
20+
21+
public static getAppRoot() {
22+
return this._appRoot;
23+
}
24+
25+
public static setAppRoot(appRoot: string) {
26+
this._appRoot = appRoot;
27+
}
28+
29+
constructor() {
30+
if (!ExtensionClient.getAppRoot()) {
31+
throw new Error(`Unable to connect to extension host. App root is '${ExtensionClient.getAppRoot()}'`);
32+
}
33+
34+
this._idCounter = 0;
35+
this._pendingRequests = {};
36+
37+
ipc.config.id = 'debug-adpater-' + process.pid;
38+
ipc.config.retry = 1500;
39+
40+
this._ipcClientInitialized = new Promise((res, rej) => {
41+
ipc.connectTo(
42+
'extHost',
43+
path.join(ExtensionClient.getAppRoot(), 'temp-nativescript-vscode-extension-pipe-handle'),
44+
() => {
45+
ipc.of.extHost.on('connect', () => {
46+
res();
47+
});
48+
ipc.of.extHost.on('extension-protocol-message', (response: extProtocol.Response) => {
49+
(<(result: Object) => void>this._pendingRequests[response.requestId])(response.result);
50+
});
51+
}
52+
);
53+
});
54+
}
55+
56+
private callRemoteMethod(method: string, args: Object): Promise<Object> {
57+
let request: extProtocol.Request = { id: 'req' + (++this._idCounter), method: method, args: args };
58+
return new Promise<Object>((res, rej) => {
59+
this._pendingRequests[request.id] = res;
60+
ipc.of.extHost.emit('extension-protocol-message', request);
61+
});
62+
}
63+
64+
public analyticsLaunchDebugger(args: extProtocol.AnalyticsLaunchDebuggerArgs): Promise<any> {
65+
return this.callRemoteMethod('analyticsLaunchDebugger', args);
66+
}
67+
68+
public runRunCommand(args: extProtocol.AnalyticsRunRunCommandArgs): Promise<any> {
69+
return this.callRemoteMethod('runRunCommand', args);
70+
}
71+
}

src/services/ipc/ExtensionProtocol.ts

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
export interface Request {
2+
id: string;
3+
method: string;
4+
args: Object;
5+
}
6+
7+
export interface Response {
8+
requestId: string;
9+
result: Object;
10+
}
11+
12+
export interface AnalyticsLaunchDebuggerArgs {
13+
request: string;
14+
platform: string;
15+
emulator: boolean;
16+
}
17+
18+
export interface AnalyticsRunRunCommandArgs {
19+
platform: string;
20+
emulator: boolean;
21+
}

src/services/ipc/ExtensionServer.ts

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import * as path from 'path';
2+
import * as fs from 'fs';
3+
import * as vscode from 'vscode';
4+
import * as extProtocol from './ExtensionProtocol';
5+
import {AnalyticsService} from '../analytics/AnalyticsService';
6+
let ipc = require('node-ipc');
7+
8+
export class ExtensionServer {
9+
private static _instance: ExtensionServer;
10+
11+
private _isRunning: boolean;
12+
13+
public static getInstance() {
14+
if (!this._instance) {
15+
this._instance = new ExtensionServer();
16+
}
17+
return this._instance;
18+
}
19+
20+
constructor() {
21+
this._isRunning = false;
22+
}
23+
24+
public getPipeHandlePath(): string {
25+
return vscode.workspace.rootPath ?
26+
path.join(vscode.workspace.rootPath, 'temp-nativescript-vscode-extension-pipe-handle') :
27+
null;
28+
}
29+
30+
public start() {
31+
if (!this._isRunning) {
32+
let pipeHandlePath = this.getPipeHandlePath();
33+
if (pipeHandlePath) {
34+
ipc.serve(
35+
pipeHandlePath,
36+
() => {
37+
ipc.server.on('extension-protocol-message', (data: extProtocol.Request, socket) => {
38+
return (<Promise<Object>>this[data.method].call(this, data.args)).then(result => {
39+
let response: extProtocol.Response = { requestId: data.id, result: result };
40+
return ipc.server.emit(socket, 'extension-protocol-message', response);
41+
});
42+
});
43+
});
44+
ipc.server.start();
45+
this._isRunning = true;
46+
}
47+
}
48+
return this._isRunning;
49+
}
50+
51+
public stop() {
52+
if (this._isRunning) {
53+
ipc.server.stop();
54+
this._isRunning = false;
55+
}
56+
}
57+
58+
public isRunning() {
59+
return this._isRunning;
60+
}
61+
62+
public analyticsLaunchDebugger(args: extProtocol.AnalyticsLaunchDebuggerArgs): Promise<any> {
63+
return AnalyticsService.getInstance().launchDebugger(args.request, args.platform, args.emulator);
64+
}
65+
66+
public runRunCommand(args: extProtocol.AnalyticsRunRunCommandArgs): Promise<any> {
67+
return AnalyticsService.getInstance().runRunCommand(args.platform, args.emulator);
68+
}
69+
70+
71+
}

0 commit comments

Comments
 (0)