Skip to content

Commit aee8809

Browse files
committed
Expose caseSensitive and includePerfectMatches completer settings
1 parent 14492df commit aee8809

File tree

4 files changed

+91
-10
lines changed

4 files changed

+91
-10
lines changed

packages/jupyterlab-lsp/schema/completion.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,18 @@
5858
"default": false,
5959
"description": "In case of ties when sorting completions, should the kernel completions receive higher priority than the language server completions?"
6060
},
61+
"caseSensitive": {
62+
"title": "Case-sensitive filtering",
63+
"default": true,
64+
"type": "boolean",
65+
"description": "Should completion filtering be case-sensitive?"
66+
},
67+
"includePerfectMatches": {
68+
"title": "Include perfect matches",
69+
"default": true,
70+
"type": "boolean",
71+
"description": "Should perfect matches be included in the completion suggestions list?"
72+
},
6173
"typesMap": {
6274
"title": "Mapping of custom kernel types to valid completion kind names",
6375
"description": "Mapping used for icon selection. The kernel types (keys) are case-insensitive. Accepted values are the names of CompletionItemKind and 'Kernel' literal. The defaults aim to provide good initial experience for Julia, Python and R kernels.",

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ export class CompletionLabIntegration implements IFeatureLabIntegration {
111111
completionThemeManager.set_icons_overrides(
112112
this.settings.composite.typesMap
113113
);
114+
this.model.settings.caseSensitive = this.settings.composite.caseSensitive;
115+
this.model.settings.includePerfectMatches = this.settings.composite.includePerfectMatches;
114116
});
115117
}
116118

@@ -195,7 +197,10 @@ export class CompletionLabIntegration implements IFeatureLabIntegration {
195197
) as CompletionHandler;
196198
let completer = this.completer;
197199
completer.addClass('lsp-completer');
198-
completer.model = new LSPCompleterModel();
200+
completer.model = new LSPCompleterModel({
201+
caseSensitive: this.settings.composite.caseSensitive,
202+
includePerfectMatches: this.settings.composite.includePerfectMatches
203+
});
199204
}
200205

201206
protected get completer() {

packages/jupyterlab-lsp/src/features/completion/model.spec.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,16 @@ describe('LSPCompleterModel', () => {
3030
label: 'test',
3131
sortText: 'c'
3232
});
33+
const test_test_completion = create_dummy_item({
34+
label: 'test_test',
35+
sortText: 'test_test'
36+
});
3337

3438
beforeEach(() => {
35-
model = new LSPCompleterModel();
39+
model = new LSPCompleterModel({
40+
caseSensitive: true,
41+
includePerfectMatches: true
42+
});
3643
});
3744

3845
it('returns escaped when no query', () => {
@@ -70,6 +77,33 @@ describe('LSPCompleterModel', () => {
7077
]);
7178
});
7279

80+
it('ignores perfect matches when asked', () => {
81+
model = new LSPCompleterModel({
82+
includePerfectMatches: false
83+
});
84+
85+
model.setCompletionItems([test_a_completion, test_test_completion]);
86+
model.query = 'test';
87+
let items = model.completionItems();
88+
// should not include the perfect match 'test'
89+
expect(items.length).to.equal(1);
90+
expect(items.map(item => item.sortText)).to.deep.equal(['test_test']);
91+
});
92+
93+
it('case-sensitivity can be changed', () => {
94+
model = new LSPCompleterModel();
95+
model.setCompletionItems([test_a_completion]);
96+
model.query = 'Test';
97+
98+
model.settings.caseSensitive = true;
99+
let items = model.completionItems();
100+
expect(items.length).to.equal(0);
101+
102+
model.settings.caseSensitive = false;
103+
items = model.completionItems();
104+
expect(items.length).to.equal(1);
105+
});
106+
73107
it('filters use filterText', () => {
74108
model.setCompletionItems([jupyter_icon_completion]);
75109
// font is in filterText but not in label

packages/jupyterlab-lsp/src/features/completion/model.ts

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,13 @@ function escapeHTML(text: string) {
2727
export class GenericCompleterModel<
2828
T extends CompletionHandler.ICompletionItem
2929
> extends CompleterModel {
30-
public caseSensitive = true;
30+
public settings: GenericCompleterModel.IOptions;
31+
32+
constructor(settings: GenericCompleterModel.IOptions = {}) {
33+
super();
34+
// TODO: refactor upstream so that it does not block "options"?
35+
this.settings = { ...GenericCompleterModel.defaultOptions, ...settings };
36+
}
3137

3238
completionItems(): T[] {
3339
let query = this.query;
@@ -76,11 +82,22 @@ export class GenericCompleterModel<
7682
let filterText: string = null;
7783
let filterMatch: StringExt.IMatchResult;
7884

85+
let lowerCaseQuery = query.toLowerCase();
86+
7987
if (query) {
8088
filterText = this.getFilterText(item);
81-
filterMatch = StringExt.matchSumOfSquares(filterText, query);
82-
// ignore perfect matches (those are not useful)
83-
matched = !!filterMatch && filterText != query;
89+
if (this.settings.caseSensitive) {
90+
filterMatch = StringExt.matchSumOfSquares(filterText, query);
91+
} else {
92+
filterMatch = StringExt.matchSumOfSquares(
93+
filterText.toLowerCase(),
94+
lowerCaseQuery
95+
);
96+
}
97+
matched = !!filterMatch;
98+
if (!this.settings.includePerfectMatches) {
99+
matched = matched && filterText != query;
100+
}
84101
} else {
85102
matched = true;
86103
}
@@ -152,13 +169,26 @@ export class GenericCompleterModel<
152169
}
153170
}
154171

172+
export namespace GenericCompleterModel {
173+
export interface IOptions {
174+
/**
175+
* Whether matching should be case-sensitive (default = true)
176+
*/
177+
caseSensitive?: boolean;
178+
/**
179+
* Whether perfect matches should be included (default = true)
180+
*/
181+
includePerfectMatches?: boolean;
182+
}
183+
export const defaultOptions: IOptions = {
184+
caseSensitive: true,
185+
includePerfectMatches: true
186+
};
187+
}
188+
155189
export class LSPCompleterModel extends GenericCompleterModel<
156190
LazyCompletionItem
157191
> {
158-
constructor() {
159-
super();
160-
}
161-
162192
protected getFilterText(item: LazyCompletionItem): string {
163193
if (item.filterText) {
164194
return item.filterText;

0 commit comments

Comments
 (0)