Skip to content

Commit 49bd4ee

Browse files
committed
Add completion settings (Hinterland mode, documentation, suppression)
1 parent f305ab4 commit 49bd4ee

File tree

12 files changed

+218
-109
lines changed

12 files changed

+218
-109
lines changed

packages/jupyterlab-lsp/schema/plugin.json

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,55 @@
11
{
2+
"jupyter.lab.setting-icon": "lsp:diagnostics",
3+
"jupyter.lab.setting-icon-label": "Language integration",
24
"title": "Language Server",
35
"description": "Language Server Protocol settings.",
46
"type": "object",
7+
"definitions": {
8+
"language-server": {
9+
"title": "Language Server",
10+
"description": "Client and server configurations for a single language server",
11+
"type": "object",
12+
"default": {},
13+
"properties": {
14+
"serverSettings": {
15+
"title": "Language Server Configurations",
16+
"description": "Configuration to be sent to language server over LSP when initialized: see the specific language server's documentation for more",
17+
"type": "object",
18+
"default": {}
19+
}
20+
}
21+
},
22+
"features": {
23+
"title": "Code Intelligence Features Configuration",
24+
"description": "Configuration for the implementation of the LSP features",
25+
"type": "object",
26+
"properties": {
27+
"completion": {
28+
"type": "object",
29+
"properties": {
30+
"showDocumentation": {
31+
"type": "boolean",
32+
"default": true
33+
},
34+
"continuousHinting": {
35+
"type": "boolean",
36+
"default": false
37+
},
38+
"suppressInvokeIn": {
39+
"type": "array",
40+
"items": {
41+
"type": "string"
42+
}
43+
}
44+
}
45+
}
46+
}
47+
}
48+
},
549
"properties": {
650
"language_servers": {
751
"title": "Language Server",
852
"description": "Language-server specific configuration, keyed by implementation, e.g: \n\npyls: {\n serverSettings: {\n pyls: {\n plugins: {\n pydocstyle: {\n enabled: true\n },\n pyflakes: {\n enabled: false\n },\n flake8: {\n enabled: true\n }\n }\n }\n }\n}\n\nAlternatively, using VSCode's naming convention:\n\npyls: {\n serverSettings: {\n \"pyls.plugins.pydocstyle.enabled\": true,\n \"pyls.plugins.pyflakes.enabled\": false,\n \"pyls.plugins.flake8.enabled\": true\n }\n}",
9-
"type": "object",
1053
"default": {},
1154
"patternProperties": {
1255
".*": {
@@ -16,20 +59,16 @@
1659
"additionalProperties": {
1760
"$ref": "#/definitions/language-server"
1861
}
19-
}
20-
},
21-
"definitions": {
22-
"language-server": {
23-
"title": "Language Server",
24-
"description": "Client and server configurations for a single language server",
25-
"type": "object",
26-
"default": {},
27-
"properties": {
28-
"serverSettings": {
29-
"title": "Language Server Configurations",
30-
"description": "Configuration to be sent to language server over LSP when initialized: see the specific language server's documentation for more",
31-
"type": "object",
32-
"default": {}
62+
},
63+
"features": {
64+
"title": "LSP features",
65+
"description": "Client-side features configuration",
66+
"$ref": "#/definitions/features",
67+
"default": {
68+
"completion": {
69+
"showDocumentation": true,
70+
"continuousHinting": false,
71+
"suppressInvokeIn": ["comment", "string"]
3372
}
3473
}
3574
}

packages/jupyterlab-lsp/src/adapters/codemirror/cm_adapter.spec.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,17 @@ import {
77
} from '../jupyterlab/jl_adapter';
88
import { IRootPosition } from '../../positioning';
99
import * as CodeMirror from 'codemirror';
10-
import { CodeMirrorLSPFeature } from './feature';
10+
import { CodeMirrorLSPFeature, IFeatureSettings } from './feature';
1111
import { FileEditorFeatureTestEnvironment } from './testutils';
1212

13+
export class DummySettings implements IFeatureSettings {
14+
get(setting: string): any {
15+
}
16+
17+
set(setting: string, value: any): void {
18+
}
19+
}
20+
1321
describe('CodeMirrorAdapter', () => {
1422
let env: FileEditorFeatureTestEnvironment;
1523

@@ -46,7 +54,8 @@ describe('CodeMirrorAdapter', () => {
4654
virtual_editor.virtual_document,
4755
connection,
4856
dummy_components_manager,
49-
new StatusMessage()
57+
new StatusMessage(),
58+
new DummySettings()
5059
);
5160

5261
let adapter = new CodeMirrorAdapter(

packages/jupyterlab-lsp/src/adapters/codemirror/feature.spec.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { language_specific_overrides } from '../../magics/defaults';
2222
import { foreign_code_extractors } from '../../extractors/defaults';
2323
import { NotebookModel } from '@jupyterlab/notebook';
2424
import { PageConfig } from '@jupyterlab/coreutils';
25+
import { DummySettings } from "./cm_adapter.spec";
2526

2627
const js_fib_code = `function fib(n) {
2728
return n<2?n:fib(n-1)+fib(n-2);
@@ -119,7 +120,8 @@ describe('Feature', () => {
119120
virtual_editor.virtual_document,
120121
connection,
121122
dummy_components_manager,
122-
new StatusMessage()
123+
new StatusMessage(),
124+
new DummySettings()
123125
);
124126
}
125127

packages/jupyterlab-lsp/src/adapters/codemirror/feature.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@ export interface ILSPFeature {
9191
): void;
9292
}
9393

94+
export interface IFeatureSettings {
95+
get(setting: string): any;
96+
set(setting: string, value: any): void;
97+
}
98+
9499
export interface IEditorRange {
95100
start: IEditorPosition;
96101
end: IEditorPosition;
@@ -124,7 +129,8 @@ export abstract class CodeMirrorLSPFeature implements ILSPFeature {
124129
public virtual_document: VirtualDocument,
125130
public connection: LSPConnection,
126131
public jupyterlab_components: IJupyterLabComponentsManager,
127-
public status_message: StatusMessage
132+
public status_message: StatusMessage,
133+
public settings: IFeatureSettings
128134
) {
129135
this.editor_handlers = new Map();
130136
this.connection_handlers = new Map();

packages/jupyterlab-lsp/src/adapters/codemirror/features/completion.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ export class Completion extends CodeMirrorLSPFeature {
2323
afterChange(change: CodeMirror.EditorChange): void {
2424
// TODO: maybe the completer could be kicked off in the handleChange() method directly; signature help still
2525
// requires an up-to-date virtual document on the LSP side, so we need to wait for sync.
26+
if (change.text && change.text[0].length == 1 && this.settings.get('continuousHinting') as boolean) {
27+
this.jupyterlab_components.invoke_completer(
28+
CompletionTriggerKind.Invoked
29+
)
30+
return;
31+
}
32+
2633
let last_character = this.extract_last_character(change);
2734
if (this.completionCharacters.indexOf(last_character) > -1) {
2835
this.virtual_editor.console.log(

packages/jupyterlab-lsp/src/adapters/jupyterlab/components/completion.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { LSPConnection } from '../../../connection';
2121
import { Session } from '@jupyterlab/services';
2222
import ICompletionItemsResponseType = CompletionHandler.ICompletionItemsResponseType;
2323
import { kernelIcon } from '@jupyterlab/ui-components';
24+
import { IFeatureSettings } from "../../codemirror/feature";
2425

2526
/**
2627
* A LSP connector for completion handlers.
@@ -33,22 +34,27 @@ export class LSPConnector
3334
private _context_connector: ContextConnector;
3435
private _kernel_connector: KernelConnector;
3536
private _kernel_and_context_connector: CompletionConnector;
36-
protected options: LSPConnector.IOptions;
3737

3838
// signal that this is the new type connector (providing completion items)
3939
responseType = ICompletionItemsResponseType;
4040

4141
virtual_editor: VirtualEditor;
4242
private trigger_kind: CompletionTriggerKind;
43-
// TODO expose this in user settings
44-
private suppress_auto_invoke_in = ['comment', 'string'];
43+
44+
private get suppress_auto_invoke_in(): string[] {
45+
return this.options.settings.get("suppressInvokeIn")
46+
}
47+
48+
private get should_show_documentation(): boolean {
49+
return this.options.settings.get("showDocumentation")
50+
}
4551

4652
/**
4753
* Create a new LSP connector for completion requests.
4854
*
4955
* @param options - The instantiation options for the LSP connector.
5056
*/
51-
constructor(options: LSPConnector.IOptions) {
57+
constructor(protected options: LSPConnector.IOptions) {
5258
this._editor = options.editor;
5359
this._connections = options.connections;
5460
this.virtual_editor = options.virtual_editor;
@@ -60,7 +66,6 @@ export class LSPConnector
6066
kernel_options
6167
);
6268
}
63-
this.options = options;
6469
}
6570

6671
dispose() {
@@ -216,14 +221,17 @@ export class LSPConnector
216221
let prefix = token.value.slice(0, position_in_token + 1);
217222
let all_non_prefixed = true;
218223
let items: CompletionHandler.ICompletionItem[] = [];
224+
const show_documentation = this.should_show_documentation;
219225
lspCompletionItems.forEach(match => {
220226
let completionItem = {
221227
label: match.label,
222228
insertText: match.insertText,
223229
type: match.kind ? completionItemKindNames[match.kind] : '',
224-
documentation: lsProtocol.MarkupContent.is(match.documentation)
230+
documentation: show_documentation ? (
231+
lsProtocol.MarkupContent.is(match.documentation)
225232
? match.documentation.value
226-
: match.documentation,
233+
: match.documentation
234+
) : null,
227235
filterText: match.filterText,
228236
deprecated: match.deprecated,
229237
data: { ...match }
@@ -386,6 +394,8 @@ export namespace LSPConnector {
386394
*/
387395
connections: Map<VirtualDocument.id_path, LSPConnection>;
388396

397+
settings: IFeatureSettings;
398+
389399
session?: Session.ISessionConnection;
390400
}
391401
}

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

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,11 @@ import { FileEditor } from '@jupyterlab/fileeditor';
33
import { IDocumentWidget } from '@jupyterlab/docregistry';
44
import { FileEditorJumper } from '@krassowski/jupyterlab_go_to_definition/lib/jumpers/fileeditor';
55
import * as CodeMirror from 'codemirror';
6-
import { JupyterFrontEnd } from '@jupyterlab/application';
7-
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
86
import { CodeMirrorEditor } from '@jupyterlab/codemirror';
9-
import { ICompletionManager } from '@jupyterlab/completer';
107
import { LSPConnector } from './components/completion';
118
import { CodeEditor } from '@jupyterlab/codeeditor';
129
import { VirtualFileEditor } from '../../virtual/editors/file_editor';
13-
import { DocumentConnectionManager } from '../../connection_manager';
10+
import { LSPExtension } from "../../index";
1411

1512
export class FileEditorAdapter extends JupyterLabWidgetAdapter {
1613
editor: FileEditor;
@@ -44,19 +41,14 @@ export class FileEditorAdapter extends JupyterLabWidgetAdapter {
4441
}
4542

4643
constructor(
44+
extension: LSPExtension,
4745
editor_widget: IDocumentWidget<FileEditor>,
48-
jumper: FileEditorJumper,
49-
app: JupyterFrontEnd,
50-
protected completion_manager: ICompletionManager,
51-
rendermime_registry: IRenderMimeRegistry,
52-
connection_manager: DocumentConnectionManager
46+
jumper: FileEditorJumper
5347
) {
5448
super(
55-
app,
49+
extension,
5650
editor_widget,
57-
rendermime_registry,
5851
'completer:invoke-file',
59-
connection_manager
6052
);
6153
this.jumper = jumper;
6254
this.editor = editor_widget.content;
@@ -84,9 +76,10 @@ export class FileEditorAdapter extends JupyterLabWidgetAdapter {
8476
this.current_completion_connector = new LSPConnector({
8577
editor: this.editor.editor,
8678
connections: this.connection_manager.connections,
87-
virtual_editor: this.virtual_editor
79+
virtual_editor: this.virtual_editor,
80+
settings: this.completion_settings,
8881
});
89-
this.completion_manager.register({
82+
this.extension.completion_manager.register({
9083
connector: this.current_completion_connector,
9184
editor: this.editor.editor,
9285
parent: this.widget

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

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { Diagnostics } from '../codemirror/features/diagnostics';
2222
import { Highlights } from '../codemirror/features/highlights';
2323
import { Hover } from '../codemirror/features/hover';
2424
import { Signature } from '../codemirror/features/signature';
25-
import { ILSPFeatureConstructor, ILSPFeature } from '../codemirror/feature';
25+
import { ILSPFeatureConstructor, ILSPFeature, IFeatureSettings } from '../codemirror/feature';
2626
import { JumpToDefinition } from '../codemirror/features/jump_to';
2727
import { ICommandContext } from '../../command_manager';
2828
import { JSONObject } from '@lumino/coreutils';
@@ -32,6 +32,35 @@ import {
3232
ISocketConnectionOptions
3333
} from '../../connection_manager';
3434
import { Rename } from '../codemirror/features/rename';
35+
import { LSPExtension } from "../../index";
36+
import { ISettingRegistry } from "@jupyterlab/settingregistry";
37+
38+
39+
class LabFeatureSettings implements IFeatureSettings {
40+
41+
private key = 'features';
42+
43+
constructor(private extension: LSPExtension, protected feature_name: string) {}
44+
45+
protected get lab_settings(): ISettingRegistry.ISettings {
46+
return this.extension.settings;
47+
}
48+
49+
protected get_obj(): any {
50+
return (this.lab_settings.get(this.key).composite as any)[this.feature_name.toLowerCase()];
51+
}
52+
53+
get(setting: string) {
54+
return this.get_obj()[setting];
55+
}
56+
57+
set(setting: string, value: any) {
58+
let obj = this.get_obj();
59+
obj[setting] = value;
60+
this.lab_settings.set(this.key, obj).catch(console.warn)
61+
}
62+
}
63+
3564

3665
export const lsp_features: Array<ILSPFeatureConstructor> = [
3766
Completion,
@@ -119,18 +148,28 @@ export abstract class JupyterLabWidgetAdapter
119148
public status_message: StatusMessage;
120149
public isDisposed = false;
121150

151+
protected app: JupyterFrontEnd;
152+
protected rendermime_registry: IRenderMimeRegistry;
153+
/**
154+
* Completions are extraordinary in that they require separate settings for the completion component
155+
* which is heavily integrated with JupyterLab rather than with the CodeMirror
156+
* @protected
157+
*/
158+
protected completion_settings: IFeatureSettings;
159+
122160
protected constructor(
123-
protected app: JupyterFrontEnd,
161+
protected extension: LSPExtension,
124162
protected widget: IDocumentWidget,
125-
protected rendermime_registry: IRenderMimeRegistry,
126163
invoke: string,
127-
connection_manager: DocumentConnectionManager
128164
) {
165+
this.app = extension.app;
166+
this.rendermime_registry = extension.rendermime_registry;
167+
this.connection_manager = extension.connection_manager;
129168
this.document_connected = new Signal(this);
130169
this.invoke_command = invoke;
131170
this.adapters = new Map();
132171
this.status_message = new StatusMessage();
133-
this.connection_manager = connection_manager;
172+
this.completion_settings = new LabFeatureSettings(this.extension, 'completion')
134173

135174
// set up signal connections
136175
this.widget.context.saveState.connect(this.on_save_state, this);
@@ -473,7 +512,8 @@ export abstract class JupyterLabWidgetAdapter
473512
virtual_document,
474513
connection,
475514
this,
476-
this.status_message
515+
this.status_message,
516+
new LabFeatureSettings(this.extension, feature_type.name)
477517
);
478518
adapter_features.push(feature);
479519
}

0 commit comments

Comments
 (0)