Skip to content

Commit f51149b

Browse files
Merge pull request #101 from ManuelHentschel/protocolServer
Protocol server
2 parents 9374907 + 419a6e9 commit f51149b

File tree

7 files changed

+286
-287
lines changed

7 files changed

+286
-287
lines changed

package-lock.json

Lines changed: 8 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,9 @@
7373
"debuggers": [
7474
{
7575
"type": "R-Debugger",
76-
"languages": ["r"],
76+
"languages": [
77+
"r"
78+
],
7779
"label": "R Debugger",
7880
"program": "./out/debugAdapter.js",
7981
"runtime": "node",
@@ -367,14 +369,15 @@
367369
"devDependencies": {
368370
"@types/glob": "^7.1.3",
369371
"@types/mocha": "^7.0.2",
370-
"@types/node": "^13.13.25",
372+
"@types/node": "^13.13.27",
371373
"@types/vscode": "^1.50.0",
372374
"@typescript-eslint/eslint-plugin": "^2.34.0",
373375
"@typescript-eslint/parser": "^2.34.0",
374376
"eslint": "^6.8.0",
375377
"glob": "^7.1.6",
376378
"mocha": "^7.2.0",
377379
"typescript": "^3.9.7",
380+
"vscode-debugprotocol": "^1.42.0",
378381
"vscode-test": "^1.4.0"
379382
},
380383
"dependencies": {
@@ -383,7 +386,6 @@
383386
"net": "^1.0.2",
384387
"semver": "^7.3.2",
385388
"tree-kill": "^1.2.2",
386-
"vscode-debugadapter": "^1.42.1",
387389
"winreg": "^1.2.4"
388390
},
389391
"definitions": {

src/debugAdapter.ts

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
2+
/*
3+
This file contains an implementation of vscode.Debugadapter.
4+
5+
DAP messages are received via `DebugAdapter.handleMessage` and sent via
6+
`onDidSendMessage`.
7+
8+
Most messages are simply passed to the R pacakge by calling
9+
`this.debugRuntime.dispatchRequest()`, only some requests are modified/handled
10+
in `this.dispatchRequest()`.
11+
*/
12+
13+
import { DebugRuntime } from './debugRuntime';
14+
import { DebugProtocol } from 'vscode-debugprotocol';
15+
import { InitializeRequest } from './debugProtocolModifications';
16+
import { config, getVSCodePackageVersion } from './utils';
17+
18+
import * as vscode from 'vscode';
19+
20+
import * as log from 'loglevel';
21+
const logger = log.getLogger("DebugSession");
22+
logger.setLevel(config().get<log.LogLevelDesc>('logLevelSession', 'INFO'));
23+
24+
25+
function logMessage(message: DebugProtocol.ProtocolMessage){
26+
let ret: string = '';
27+
if(message.type === 'event'){
28+
const event = <DebugProtocol.Event>message;
29+
ret = `event: ${event.event}`;
30+
} else if(message.type === 'response'){
31+
const response = <DebugProtocol.Response>message;
32+
ret = `response ${response.request_seq}: ${response.command}`;
33+
} else{
34+
ret = `unknown protocol message type: ${message.type}`;
35+
}
36+
return ret;
37+
}
38+
39+
40+
export class DebugAdapter implements vscode.DebugAdapter {
41+
42+
// properties
43+
private sendMessage = new vscode.EventEmitter<DebugProtocol.ProtocolMessage>(); // used by onDidSendMessage
44+
private sequence: number = 0; // seq of messages sent to VS Code
45+
private THREAD_ID = 1; // dummy value
46+
private runtime: DebugRuntime; // actually handles requests etc. that are not forwarded
47+
private disconnectTimeout: number = config().get<number>('timeouts.startup', 1000);
48+
49+
constructor() {
50+
// construct R runtime
51+
this.runtime = new DebugRuntime();
52+
53+
// setup event handler
54+
this.runtime.on('protocolMessage', (message: DebugProtocol.ProtocolMessage) => {
55+
this.sendProtocolMessage(message);
56+
});
57+
}
58+
59+
// dummy, required by vscode.Disposable (?)
60+
public dispose(): void {};
61+
62+
// used to send messages from R to VS Code
63+
readonly onDidSendMessage: vscode.Event<DebugProtocol.ProtocolMessage> = this.sendMessage.event;
64+
65+
// used to send messages from VS Code to R
66+
public handleMessage(msg: DebugProtocol.ProtocolMessage): void {
67+
if(msg.type === 'request') {
68+
this.dispatchRequest(<DebugProtocol.Request>msg);
69+
}
70+
}
71+
72+
protected sendProtocolMessage(message: DebugProtocol.ProtocolMessage): void {
73+
logger.info(logMessage(message), message);
74+
message.seq = this.sequence++;
75+
this.sendMessage.fire(message);
76+
}
77+
78+
protected dispatchRequest(request: DebugProtocol.Request): void {
79+
// prepare response
80+
const response: DebugProtocol.Response = {
81+
command: request.command,
82+
request_seq: request.seq,
83+
seq: 0,
84+
success: true,
85+
type: 'response'
86+
};
87+
let dispatchToR: boolean = false; // the cases handled here are not sent to R
88+
let sendResponse: boolean = true; // for cases handled here, the response must also be sent from here
89+
try {
90+
switch(request.command){
91+
case 'initialize':
92+
request.arguments = request.arguments || {};
93+
request.arguments.threadId = this.THREAD_ID;
94+
request.arguments.extensionVersion = getVSCodePackageVersion();
95+
this.runtime.initializeRequest(response, request.arguments, <InitializeRequest>request);
96+
sendResponse = false;
97+
break;
98+
case 'launch':
99+
dispatchToR = true;
100+
sendResponse = false;
101+
this.runtime.writeOutput('Launch Arguments:\n' + JSON.stringify(request.arguments, undefined, 2));
102+
this.runtime.endOutputGroup();
103+
break;
104+
case 'evaluate':
105+
const matches = /^### ?[sS][tT][dD][iI][nN]\s*(.*)$/s.exec(request.arguments.expression);
106+
if(matches){
107+
// send directly to stdin, don't send request
108+
const toStdin = matches[1];
109+
logger.debug('user to stdin:\n' + toStdin);
110+
this.runtime.rSession.writeToStdin(toStdin);
111+
} else{
112+
// dispatch normally
113+
dispatchToR = true;
114+
sendResponse = false;
115+
}
116+
break;
117+
case 'disconnect':
118+
// kill R process after timeout, in case it doesn't quit successfully
119+
setTimeout(()=>{
120+
console.log('killing R...');
121+
this.runtime.killR();
122+
}, this.disconnectTimeout);
123+
dispatchToR = true;
124+
sendResponse = false;
125+
break;
126+
case 'continue':
127+
// pass info about the currently open text editor
128+
// can be used to start .vsc.debugSource(), when called from global workspace
129+
const doc = vscode.window.activeTextEditor.document;
130+
if(doc.uri.scheme === 'file'){
131+
const filename = doc.fileName;
132+
request.arguments.callDebugSource = true;
133+
request.arguments.source = {path: filename};
134+
};
135+
dispatchToR = true;
136+
sendResponse = false;
137+
break;
138+
case 'pause':
139+
// this._runtime.killR('SIGSTOP'); // doesn't work
140+
response.success = false;
141+
break;
142+
default:
143+
// request not handled here -> send to R
144+
dispatchToR = true;
145+
sendResponse = false;
146+
// end cases
147+
}
148+
} catch (e) {
149+
logger.error("Error while handling request " + request.seq + ": " + request.command);
150+
response.success = false;
151+
dispatchToR = false;
152+
sendResponse = true;
153+
}
154+
155+
// dispatch to R if not (completely) handled here
156+
if(dispatchToR){
157+
this.runtime.dispatchRequest(request);
158+
} else{
159+
logger.info("request " + request.seq + " (handled in VS Code): " + request.command, request);
160+
}
161+
162+
// send response if (completely) handled here
163+
if(sendResponse){
164+
this.sendProtocolMessage(response);
165+
}
166+
}
167+
}

0 commit comments

Comments
 (0)