Skip to content

Commit 9b5c35f

Browse files
committed
Implement setTrace, logTrace, and optional logging of messages using new API
1 parent 0c1f2b9 commit 9b5c35f

File tree

6 files changed

+178
-67
lines changed

6 files changed

+178
-67
lines changed

packages/jupyterlab-lsp/schema/plugin.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,19 @@
6060
"enum": ["debug", "log", "warn", "error"],
6161
"default": "warn",
6262
"description": "The verbosity of the console for debugging problems with this extension. Allowed values are: debug, log, warn, error."
63+
},
64+
"logAllCommunication": {
65+
"title": "Log all LSP communication with the LSP servers",
66+
"type": "boolean",
67+
"default": false,
68+
"description": "Whether all messages sent to and received from LSP servers should be logged into the console. To see these messages, set loggingLevel to debug or log. Note: Only messages handled by the new API will be shown."
69+
},
70+
"setTrace": {
71+
"title": "Ask servers to send trace notifications",
72+
"type": ["string", "null"],
73+
"enum": ["off", "messages", "verbose", null],
74+
"default": null,
75+
"description": "Whether to ask server to send logs with execution trace (for debugging). To see these messages, set loggingLevel to debug or log. Accepted values are: \"off\", \"messages\", \"verbose\". Servers are allowed to ignore this request."
6376
}
6477
},
6578
"jupyter.lab.shortcuts": []

packages/jupyterlab-lsp/src/adapters/adapter.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { JupyterFrontEnd } from '@jupyterlab/application';
2+
import { Dialog, showDialog } from '@jupyterlab/apputils';
23
import { CodeEditor } from '@jupyterlab/codeeditor';
34
import { DocumentRegistry, IDocumentWidget } from '@jupyterlab/docregistry';
45
import { ILogPayload } from '@jupyterlab/logconsole';
@@ -22,9 +23,6 @@ import { IForeignContext, VirtualDocument } from '../virtual/document';
2223
import { IVirtualEditor } from '../virtual/editor';
2324

2425
import IEditor = CodeEditor.IEditor;
25-
26-
import { Dialog, showDialog } from '@jupyterlab/apputils';
27-
2826
import IButton = Dialog.IButton;
2927
import createButton = Dialog.createButton;
3028

@@ -353,6 +351,17 @@ export abstract class WidgetAdapter<T extends IDocumentWidget> {
353351
const loggerSourceName = virtual_document.uri;
354352
const logger = this.extension.user_console.getLogger(loggerSourceName);
355353

354+
data.connection.serverNotifications['$/logTrace'].connect(
355+
(connection, message) => {
356+
this.console.log(
357+
data.connection.serverIdentifier,
358+
'trace',
359+
virtual_document.uri,
360+
message
361+
);
362+
}
363+
);
364+
356365
data.connection.serverNotifications['window/logMessage'].connect(
357366
(connection, message) => {
358367
this.console.log(

packages/jupyterlab-lsp/src/connection.ts

Lines changed: 118 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@ import type * as rpc from 'vscode-jsonrpc';
1919
import type * as lsp from 'vscode-languageserver-protocol';
2020
import type { MessageConnection } from 'vscode-ws-jsonrpc';
2121

22+
import { ILSPLogConsole } from './tokens';
2223
import { until_ready } from './utils';
2324

2425
interface ILSPOptions extends ILspOptions {
2526
serverIdentifier?: string;
27+
console: ILSPLogConsole;
2628
}
2729

2830
/**
@@ -49,7 +51,7 @@ export namespace Method {
4951
}
5052

5153
/** Server requests */
52-
export enum ServerRequests {
54+
export enum ServerRequest {
5355
REGISTER_CAPABILITY = 'client/registerCapability',
5456
SHOW_MESSAGE_REQUEST = 'window/showMessageRequest',
5557
UNREGISTER_CAPABILITY = 'client/unregisterCapability'
@@ -90,15 +92,15 @@ export interface IClientNotifyParams {
9092
}
9193

9294
export interface IServerRequestParams {
93-
[Method.ServerRequests.REGISTER_CAPABILITY]: lsp.RegistrationParams;
94-
[Method.ServerRequests.SHOW_MESSAGE_REQUEST]: lsp.ShowMessageRequestParams;
95-
[Method.ServerRequests.UNREGISTER_CAPABILITY]: lsp.UnregistrationParams;
95+
[Method.ServerRequest.REGISTER_CAPABILITY]: lsp.RegistrationParams;
96+
[Method.ServerRequest.SHOW_MESSAGE_REQUEST]: lsp.ShowMessageRequestParams;
97+
[Method.ServerRequest.UNREGISTER_CAPABILITY]: lsp.UnregistrationParams;
9698
}
9799

98100
export interface IServerResult {
99-
[Method.ServerRequests.REGISTER_CAPABILITY]: void;
100-
[Method.ServerRequests.SHOW_MESSAGE_REQUEST]: lsp.MessageActionItem | null;
101-
[Method.ServerRequests.UNREGISTER_CAPABILITY]: void;
101+
[Method.ServerRequest.REGISTER_CAPABILITY]: void;
102+
[Method.ServerRequest.SHOW_MESSAGE_REQUEST]: lsp.MessageActionItem | null;
103+
[Method.ServerRequest.UNREGISTER_CAPABILITY]: void;
102104
}
103105

104106
export interface IClientRequestParams {
@@ -134,14 +136,14 @@ export interface IClientResult {
134136
export type ServerNotifications<
135137
T extends keyof IServerNotifyParams = keyof IServerNotifyParams
136138
> = {
137-
// ISignal does not have emit, which is intended - client cannot emit server notifications.
139+
readonly // ISignal does not have emit, which is intended - client cannot emit server notifications.
138140
[key in T]: ISignal<LSPConnection, IServerNotifyParams[key]>;
139141
};
140142

141143
export type ClientNotifications<
142144
T extends keyof IClientNotifyParams = keyof IClientNotifyParams
143145
> = {
144-
// Signal has emit.
146+
readonly // Signal has emit.
145147
[key in T]: Signal<LSPConnection, IClientNotifyParams[key]>;
146148
};
147149

@@ -166,23 +168,39 @@ export interface IServerRequestHandler<
166168
export type ClientRequests<
167169
T extends keyof IClientRequestParams = keyof IClientRequestParams
168170
> = {
169-
// has async request(params) returning a promise with result.
171+
readonly // has async request(params) returning a promise with result.
170172
[key in T]: IClientRequestHandler<key>;
171173
};
172174

173175
export type ServerRequests<
174176
T extends keyof IServerRequestParams = keyof IServerRequestParams
175177
> = {
176-
// has async request(params) returning a promise with result.
178+
readonly // has async request(params) returning a promise with result.
177179
[key in T]: IServerRequestHandler<key>;
178180
};
179181

180182
class ClientRequestHandler<
181183
T extends keyof IClientRequestParams = keyof IClientRequestParams
182184
> implements IClientRequestHandler {
183-
constructor(protected connection: MessageConnection, protected method: T) {}
185+
constructor(
186+
protected connection: MessageConnection,
187+
protected method: T,
188+
protected emitter: LSPConnection
189+
) {}
184190
request(params: IClientRequestParams[T]): Promise<IClientResult[T]> {
185-
return this.connection.sendRequest(this.method, params);
191+
this.emitter.log(MessageKind.client_requested, {
192+
method: this.method,
193+
message: params
194+
});
195+
return this.connection
196+
.sendRequest(this.method, params)
197+
.then((result: IClientResult[T]) => {
198+
this.emitter.log(MessageKind.result_for_client, {
199+
method: this.method,
200+
message: params
201+
});
202+
return result;
203+
});
186204
}
187205
}
188206

@@ -205,10 +223,20 @@ class ServerRequestHandler<
205223
}
206224

207225
private handle(request: IServerRequestParams[T]): Promise<IServerResult[T]> {
226+
this.emitter.log(MessageKind.server_requested, {
227+
method: this.method,
228+
message: request
229+
});
208230
if (!this._handler) {
209231
return;
210232
}
211-
return this._handler(request, this.emitter);
233+
return this._handler(request, this.emitter).then(result => {
234+
this.emitter.log(MessageKind.response_for_server, {
235+
method: this.method,
236+
message: result
237+
});
238+
return result;
239+
});
212240
}
213241

214242
setHandler(
@@ -252,67 +280,104 @@ export const Provider: { [key: string]: keyof lsp.ServerCapabilities } = {
252280
WORKSPACE: 'workspace'
253281
};
254282

283+
type AnyMethodType =
284+
| typeof Method.ServerNotification
285+
| typeof Method.ClientNotification
286+
| typeof Method.ClientRequest
287+
| typeof Method.ServerRequest;
288+
type AnyMethod =
289+
| Method.ServerNotification
290+
| Method.ClientNotification
291+
| Method.ClientRequest
292+
| Method.ServerRequest;
293+
294+
function createMethodMap<T, H, U extends keyof T = keyof T>(
295+
methods: AnyMethodType,
296+
handlerFactory: (method: U) => H
297+
) {
298+
const result: { [key in U]?: H } = {};
299+
for (let method of Object.values(methods)) {
300+
result[method as U] = handlerFactory(method as U);
301+
}
302+
return result as T;
303+
}
304+
305+
enum MessageKind {
306+
client_notified_server,
307+
server_notified_client,
308+
server_requested,
309+
client_requested,
310+
result_for_client,
311+
response_for_server
312+
}
313+
314+
interface IMessageLog<T extends AnyMethod = AnyMethod> {
315+
method: T;
316+
message: any;
317+
}
318+
255319
export class LSPConnection extends LspWsConnection {
256320
protected documentsToOpen: IDocumentInfo[];
257321
public serverIdentifier: string;
258322

259-
public serverNotifications: ServerNotifications;
260323
public clientNotifications: ClientNotifications;
324+
public serverNotifications: ServerNotifications;
261325
public clientRequests: ClientRequests;
262326
public serverRequests: ServerRequests;
327+
protected console: ILSPLogConsole;
328+
public logAllCommunication: boolean;
329+
330+
public log(kind: MessageKind, message: IMessageLog) {
331+
if (this.logAllCommunication) {
332+
this.console.log(kind, message);
333+
}
334+
}
263335

264336
protected constructNotificationHandlers<
265-
T extends ServerNotifications | ClientNotifications,
266-
U extends keyof T = keyof T
337+
T extends ServerNotifications | ClientNotifications
267338
>(
268339
methods: typeof Method.ServerNotification | typeof Method.ClientNotification
269340
) {
270-
const result: { [key in U]?: Signal<any, any> } = {};
271-
for (let method of Object.values(methods)) {
272-
result[method as U] = new Signal<any, any>(this);
273-
}
274-
return result as T;
341+
return createMethodMap<T, Signal<any, any>>(
342+
methods,
343+
() => new Signal<any, any>(this)
344+
);
275345
}
276346

277347
protected constructClientRequestHandler<
278348
T extends ClientRequests,
279349
U extends keyof T = keyof T
280350
>(methods: typeof Method.ClientRequest) {
281-
const result: { [key in U]?: IClientRequestHandler } = {};
282-
for (let method of Object.values(methods)) {
283-
result[method as U] = new ClientRequestHandler(
284-
this.connection,
285-
(method as U) as any
286-
);
287-
}
288-
return result as T;
351+
return createMethodMap<T, IClientRequestHandler>(
352+
methods,
353+
method =>
354+
new ClientRequestHandler(this.connection, (method as U) as any, this)
355+
);
289356
}
290357

291358
protected constructServerRequestHandler<
292359
T extends ServerRequests,
293360
U extends keyof T = keyof T
294-
>(methods: typeof Method.ServerRequests) {
295-
const result: { [key in U]?: IServerRequestHandler } = {};
296-
for (let method of Object.values(methods)) {
297-
result[method as U] = new ServerRequestHandler(
298-
this.connection,
299-
(method as U) as any,
300-
this
301-
);
302-
}
303-
return result as T;
361+
>(methods: typeof Method.ServerRequest) {
362+
return createMethodMap<T, IServerRequestHandler>(
363+
methods,
364+
method =>
365+
new ServerRequestHandler(this.connection, (method as U) as any, this)
366+
);
304367
}
305368

306369
constructor(options: ILSPOptions) {
307370
super(options);
371+
this.logAllCommunication = false;
308372
this.serverIdentifier = options?.serverIdentifier;
373+
this.console = options.console.scope(this.serverIdentifier + ' connection');
309374
this.documentsToOpen = [];
310-
this.serverNotifications = this.constructNotificationHandlers<
311-
ServerNotifications
312-
>(Method.ServerNotification);
313375
this.clientNotifications = this.constructNotificationHandlers<
314376
ClientNotifications
315377
>(Method.ClientNotification);
378+
this.serverNotifications = this.constructNotificationHandlers<
379+
ServerNotifications
380+
>(Method.ServerNotification);
316381
}
317382

318383
sendOpenWhenReady(documentInfo: IDocumentInfo) {
@@ -323,18 +388,12 @@ export class LSPConnection extends LspWsConnection {
323388
}
324389
}
325390

326-
sendInitialize() {
327-
super.sendInitialize();
328-
}
329-
330391
protected onServerInitialized(params: lsp.InitializeResult) {
392+
this.afterInitialized();
331393
super.onServerInitialized(params);
332394
while (this.documentsToOpen.length) {
333395
this.sendOpen(this.documentsToOpen.pop());
334396
}
335-
// TODO: move to send Initialize after disabling overwrites in ws-connection
336-
// or maybe move the code there? How to handle logging without bringing in lumino signals?
337-
this.afterInitialized();
338397
}
339398

340399
protected afterInitialized() {
@@ -343,6 +402,10 @@ export class LSPConnection extends LspWsConnection {
343402
) as (keyof ServerNotifications)[]) {
344403
const signal = this.serverNotifications[method] as Signal<any, any>;
345404
this.connection.onNotification(method, params => {
405+
this.log(MessageKind.server_notified_client, {
406+
method,
407+
message: params
408+
});
346409
signal.emit(params);
347410
});
348411
}
@@ -352,6 +415,10 @@ export class LSPConnection extends LspWsConnection {
352415
) as (keyof ClientNotifications)[]) {
353416
const signal = this.clientNotifications[method] as Signal<any, any>;
354417
signal.connect((emitter, params) => {
418+
this.log(MessageKind.client_notified_server, {
419+
method,
420+
message: params
421+
});
355422
this.connection.sendNotification(method, params);
356423
});
357424
}
@@ -360,7 +427,7 @@ export class LSPConnection extends LspWsConnection {
360427
Method.ClientRequest
361428
);
362429
this.serverRequests = this.constructServerRequestHandler<ServerRequests>(
363-
Method.ServerRequests
430+
Method.ServerRequest
364431
);
365432

366433
this.serverRequests['client/registerCapability'].setHandler(
@@ -377,8 +444,6 @@ export class LSPConnection extends LspWsConnection {
377444
}
378445
}
379446
);
380-
381-
// TODO log event
382447
}
383448
);
384449

@@ -392,8 +457,6 @@ export class LSPConnection extends LspWsConnection {
392457
);
393458
}
394459
);
395-
396-
// TODO log event
397460
}
398461
);
399462
}

0 commit comments

Comments
 (0)