Skip to content

Commit a9e080b

Browse files
authored
Merge pull request #588 from krassowski/add-priority
Add priority setting for servers handling the same language
2 parents ffd47d3 + a5d515e commit a9e080b

File tree

12 files changed

+187
-83
lines changed

12 files changed

+187
-83
lines changed

CHANGELOG.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
## Changelog
22

3-
### (unreleased)
3+
### `@krassowski/jupyterlab-lsp 3.7.0` (unreleased)
44

5-
- Add ability to deactivate Kernel completions or LSP completion through the settings.
6-
- workaround url-parse issue causing problems when using JupyterLab 3.0.15 [#599]
5+
- features:
6+
7+
- add ability to deactivate Kernel completions or LSP completion through the settings ([#586], thanks @Carreau)
8+
- allow to set a priority for LSP server, allowing to choose which server to use when multiple servers are installed ([#588])
9+
10+
- bug fixes:
11+
- workaround url-parse issue causing problems when using JupyterLab 3.0.15 [#599]
712

13+
[#586]: https://github.com/krassowski/jupyterlab-lsp/pull/586
14+
[#588]: https://github.com/krassowski/jupyterlab-lsp/pull/588
815
[#599]: https://github.com/krassowski/jupyterlab-lsp/pull/599
916

1017
### `jupyter-lsp 1.2.0` (2021-04-26)

atest/03_Notebook.robot

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ Moving Cells Around
4747
Foreign Extractors
4848
${file} = Set Variable Foreign extractors.ipynb
4949
Configure JupyterLab Plugin
50-
... {"language_servers": {"texlab": {"serverSettings": {"latex.lint.onChange": true}}, "bash-langauge-server": {"bashIde.highlightParsingErrors": true}}}
50+
... {"language_servers": {"texlab": {"serverSettings": {"latex.lint.onChange": true}}, "bash-langauge-server": {"bashIde.highlightParsingErrors": true}}, "pylsp": {"priority": 1000}}
5151
Capture Page Screenshot 10-configured.png
5252
Reset Application State
5353
Setup Notebook Python ${file}

docs/Language Servers.ipynb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,9 @@
137137
"\n",
138138
"These servers have support for notebooks and file editors. The `pyls` and\n",
139139
"`r-languageserver` are well-tested, while `jedi` and `Julia` servers are\n",
140-
"experimental. Please only install one language server per language (`jedi` or\n",
141-
"`pyls` for Python - not both)."
140+
"experimental. If you choose to install multiple language servers for the same\n",
141+
"language, the one with the highest `priority` (which can be set in the _Advanced\n",
142+
"Settings Editor_) will be used."
142143
]
143144
},
144145
{

packages/jupyterlab-lsp/schema/plugin.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@
1616
"description": "Configuration to be sent to language server over LSP when initialized: see the specific language server's documentation for more",
1717
"type": "object",
1818
"default": {}
19+
},
20+
"priority": {
21+
"title": "Priority of the server",
22+
"description": "When multiple servers match specific document/language, the server with the highest priority will be used",
23+
"type": "number",
24+
"default": 50,
25+
"minimum": 1
1926
}
2027
}
2128
}

packages/jupyterlab-lsp/src/components/statusbar.tsx

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,12 @@ import { LSPConnection } from '../connection';
3535
import { DocumentConnectionManager } from '../connection_manager';
3636
import { SERVER_EXTENSION_404 } from '../errors';
3737
import { LanguageServerManager } from '../manager';
38-
import { ILSPAdapterManager, ILanguageServerManager } from '../tokens';
38+
import {
39+
ILSPAdapterManager,
40+
ILanguageServerManager,
41+
TSessionMap,
42+
TLanguageServerId
43+
} from '../tokens';
3944
import { VirtualDocument, collect_documents } from '../virtual/document';
4045

4146
import { codeCheckIcon, codeClockIcon, codeWarningIcon } from './icons';
@@ -464,34 +469,44 @@ export namespace LSPStatus {
464469
}, this);
465470
}
466471

467-
get available_servers(): Array<SCHEMA.LanguageServerSession> {
468-
return Array.from(this.language_server_manager.sessions.values());
472+
get available_servers(): TSessionMap {
473+
return this.language_server_manager.sessions;
469474
}
470475

471476
get supported_languages(): Set<string> {
472477
const languages = new Set<string>();
473-
for (let server of this.available_servers) {
478+
for (let server of this.available_servers.values()) {
474479
for (let language of server.spec.languages) {
475480
languages.add(language.toLocaleLowerCase());
476481
}
477482
}
478483
return languages;
479484
}
480485

481-
private is_server_running(server: SCHEMA.LanguageServerSession): boolean {
482-
for (let language of server.spec.languages) {
483-
if (this.detected_languages.has(language.toLocaleLowerCase())) {
486+
private is_server_running(
487+
id: TLanguageServerId,
488+
server: SCHEMA.LanguageServerSession
489+
): boolean {
490+
for (const language of this.detected_languages) {
491+
const matchedServers = this.language_server_manager.getMatchingServers({
492+
language
493+
});
494+
// TODO server.status === "started" ?
495+
// TODO update once multiple servers are allowed
496+
if (matchedServers.length && matchedServers[0] === id) {
484497
return true;
485498
}
486499
}
487-
return false;
488500
}
489501

490502
get documents_by_server(): Map<
491503
SCHEMA.LanguageServerSession,
492504
Map<string, VirtualDocument[]>
493505
> {
494-
let data = new Map();
506+
let data = new Map<
507+
SCHEMA.LanguageServerSession,
508+
Map<string, VirtualDocument[]>
509+
>();
495510
if (!this.adapter?.virtual_editor) {
496511
return data;
497512
}
@@ -501,19 +516,17 @@ export namespace LSPStatus {
501516

502517
for (let document of documents.values()) {
503518
let language = document.language.toLocaleLowerCase();
504-
let servers = this.available_servers.filter(
505-
server => server.spec.languages.indexOf(language) !== -1
519+
let server_ids = this._connection_manager.language_server_manager.getMatchingServers(
520+
{ language: document.language }
506521
);
507-
if (servers.length > 1) {
508-
console.warn('More than one server per language for', language);
509-
}
510-
if (servers.length === 0) {
522+
if (server_ids.length === 0) {
511523
continue;
512524
}
513-
let server = servers[0];
525+
// For now only use the server with the highest priority
526+
let server = this.language_server_manager.sessions.get(server_ids[0]);
514527

515528
if (!data.has(server)) {
516-
data.set(server, new Map<string, VirtualDocument>());
529+
data.set(server, new Map<string, VirtualDocument[]>());
517530
}
518531

519532
let documents_map = data.get(server);
@@ -529,9 +542,9 @@ export namespace LSPStatus {
529542
}
530543

531544
get servers_available_not_in_use(): Array<SCHEMA.LanguageServerSession> {
532-
return this.available_servers.filter(
533-
server => !this.is_server_running(server)
534-
);
545+
return [...this.available_servers.entries()]
546+
.filter(([id, server]) => !this.is_server_running(id, server))
547+
.map(([id, server]) => server);
535548
}
536549

537550
get detected_languages(): Set<string> {
@@ -577,10 +590,11 @@ export namespace LSPStatus {
577590

578591
detected_documents.forEach((document, uri) => {
579592
let connection = this._connection_manager.connections.get(uri);
580-
let server_id = this._connection_manager.language_server_manager.getServerId(
593+
let server_ids = this._connection_manager.language_server_manager.getMatchingServers(
581594
{ language: document.language }
582595
);
583-
if (server_id !== null) {
596+
597+
if (server_ids.length !== 0) {
584598
documents_with_known_servers.add(document);
585599
}
586600
if (!connection) {

packages/jupyterlab-lsp/src/connection.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,17 @@ import * as lsProtocol from 'vscode-languageserver-protocol';
1212

1313
import { until_ready } from './utils';
1414

15-
interface ILSPOptions extends ILspOptions {}
15+
interface ILSPOptions extends ILspOptions {
16+
serverIdentifier?: string;
17+
}
1618

1719
export class LSPConnection extends LspWsConnection {
1820
protected documentsToOpen: IDocumentInfo[];
21+
public serverIdentifier: string;
1922

2023
constructor(options: ILSPOptions) {
2124
super(options);
25+
this.serverIdentifier = options?.serverIdentifier;
2226
this.documentsToOpen = [];
2327
}
2428

packages/jupyterlab-lsp/src/connection_manager.ts

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { PageConfig, URLExt } from '@jupyterlab/coreutils';
22
import { Signal } from '@lumino/signaling';
3+
import type * as protocol from 'vscode-languageserver-protocol';
34

45
import type * as ConnectionModuleType from './connection';
56
import {
67
ILSPLogConsole,
7-
ILanguageServerConfiguration,
88
ILanguageServerManager,
99
TLanguageServerConfigurations,
10-
TLanguageServerId
10+
TLanguageServerId,
11+
TServerKeys
1112
} from './tokens';
1213
import { expandDottedPaths, sleep, until_ready } from './utils';
1314
import { IForeignContext, VirtualDocument } from './virtual/document';
@@ -134,9 +135,14 @@ export class DocumentConnectionManager {
134135
language
135136
);
136137

137-
const language_server_id = this.language_server_manager.getServerId({
138+
const matchingServers = this.language_server_manager.getMatchingServers({
138139
language
139140
});
141+
this.console.debug('Matching servers: ', matchingServers);
142+
143+
// for now use only the server with the highest priority.
144+
const language_server_id =
145+
matchingServers.length === 0 ? null : matchingServers[0];
140146

141147
// lazily load 1) the underlying library (1.5mb) and/or 2) a live WebSocket-
142148
// like connection: either already connected or potentially in the process
@@ -161,13 +167,21 @@ export class DocumentConnectionManager {
161167
* "serverSettings" keyword in the setting registry. New keywords can
162168
* be added and extra functionality implemented here when needed.
163169
*/
164-
public updateServerConfigurations(allServerSettings: any) {
165-
for (let language_server_id in allServerSettings) {
166-
const parsedSettings = expandDottedPaths(
167-
allServerSettings[language_server_id].serverSettings
168-
);
170+
public updateServerConfigurations(
171+
allServerSettings: TLanguageServerConfigurations
172+
) {
173+
let language_server_id: TServerKeys;
174+
this.language_server_manager.setConfiguration(allServerSettings);
175+
176+
for (language_server_id in allServerSettings) {
177+
if (!allServerSettings.hasOwnProperty(language_server_id)) {
178+
continue;
179+
}
180+
const rawSettings = allServerSettings[language_server_id];
169181

170-
const serverSettings: ILanguageServerConfiguration = {
182+
const parsedSettings = expandDottedPaths(rawSettings.serverSettings);
183+
184+
const serverSettings: protocol.DidChangeConfigurationParams = {
171185
settings: parsedSettings
172186
};
173187

@@ -351,9 +365,14 @@ export namespace DocumentConnectionManager {
351365
? rootUri
352366
: virtualDocumentsUri;
353367

354-
const language_server_id = Private.getLanguageServerManager().getServerId({
355-
language
356-
});
368+
// for now take the best match only
369+
const matchingServers = Private.getLanguageServerManager().getMatchingServers(
370+
{
371+
language
372+
}
373+
);
374+
const language_server_id =
375+
matchingServers.length === 0 ? null : matchingServers[0];
357376

358377
if (language_server_id === null) {
359378
throw `No language server installed for language ${language}`;
@@ -440,7 +459,8 @@ namespace Private {
440459
const connection = new LSPConnection({
441460
languageId: language,
442461
serverUri: uris.server,
443-
rootUri: uris.base
462+
rootUri: uris.base,
463+
serverIdentifier: language_server_id
444464
});
445465
// TODO: remove remaining unbounded users of connection.on
446466
connection.setMaxListeners(999);
@@ -456,7 +476,7 @@ namespace Private {
456476

457477
export function updateServerConfiguration(
458478
language_server_id: TLanguageServerId,
459-
settings: ILanguageServerConfiguration
479+
settings: protocol.DidChangeConfigurationParams
460480
): void {
461481
const connection = _connections.get(language_server_id);
462482
if (connection) {

packages/jupyterlab-lsp/src/editor_integration/testutils.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ import { EditorAdapter } from './editor_adapter';
5454
import createNotebookPanel = NBTestUtils.createNotebookPanel;
5555
import IEditor = CodeEditor.IEditor;
5656

57+
const DEFAULT_SERVER_ID = 'pylsp';
58+
5759
export interface ITestEnvironment {
5860
document_options: VirtualDocument.IOptions;
5961

@@ -71,7 +73,7 @@ export interface ITestEnvironment {
7173
export class MockLanguageServerManager extends LanguageServerManager {
7274
async fetchSessions() {
7375
this._sessions = new Map();
74-
this._sessions.set('pyls', {
76+
this._sessions.set(DEFAULT_SERVER_ID, {
7577
spec: {
7678
languages: ['python']
7779
}
@@ -111,7 +113,9 @@ export class MockExtension implements ILSPExtension {
111113
this.app = null;
112114
this.feature_manager = new FeatureManager();
113115
this.editor_type_manager = new VirtualEditorManager();
114-
this.language_server_manager = new MockLanguageServerManager({});
116+
this.language_server_manager = new MockLanguageServerManager({
117+
console: new BrowserConsole()
118+
});
115119
this.connection_manager = new DocumentConnectionManager({
116120
language_server_manager: this.language_server_manager,
117121
console: new BrowserConsole()
@@ -240,7 +244,8 @@ function FeatureSupport<TBase extends TestEnvironmentConstructor>(Base: TBase) {
240244
return new LSPConnection({
241245
languageId: this.document_options.language,
242246
serverUri: 'ws://localhost:8080',
243-
rootUri: 'file:///unit-test'
247+
rootUri: 'file:///unit-test',
248+
serverIdentifier: DEFAULT_SERVER_ID
244249
});
245250
}
246251

packages/jupyterlab-lsp/src/index.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,10 +146,12 @@ export class LSPExtension implements ILSPExtension {
146146
status_bar: IStatusBar | null
147147
) {
148148
const trans = (translator || nullTranslator).load('jupyterlab-lsp');
149-
this.language_server_manager = new LanguageServerManager({});
149+
this.language_server_manager = new LanguageServerManager({
150+
console: this.console.scope('LanguageServerManager')
151+
});
150152
this.connection_manager = new DocumentConnectionManager({
151153
language_server_manager: this.language_server_manager,
152-
console: this.console
154+
console: this.console.scope('DocumentConnectionManager')
153155
});
154156

155157
const statusButtonExtension = new StatusButtonExtension({

0 commit comments

Comments
 (0)