@theia/core 1.70.0-next.26 → 1.70.0-next.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/browser/catalog.json +19 -3
- package/lib/browser/quick-input/quick-input-service.spec.js +53 -9
- package/lib/browser/quick-input/quick-input-service.spec.js.map +1 -1
- package/lib/browser/tree/fuzzy-search.d.ts +1 -60
- package/lib/browser/tree/fuzzy-search.d.ts.map +1 -1
- package/lib/browser/tree/fuzzy-search.js +3 -58
- package/lib/browser/tree/fuzzy-search.js.map +1 -1
- package/lib/browser/window/default-window-service.d.ts +2 -1
- package/lib/browser/window/default-window-service.d.ts.map +1 -1
- package/lib/browser/window/default-window-service.js +5 -1
- package/lib/browser/window/default-window-service.js.map +1 -1
- package/lib/browser/window/test/mock-window-service.d.ts +2 -1
- package/lib/browser/window/test/mock-window-service.d.ts.map +1 -1
- package/lib/browser/window/test/mock-window-service.js +2 -1
- package/lib/browser/window/test/mock-window-service.js.map +1 -1
- package/lib/browser/window/window-service.d.ts +8 -1
- package/lib/browser/window/window-service.d.ts.map +1 -1
- package/lib/common/fuzzy-match-utils.d.ts +27 -0
- package/lib/common/fuzzy-match-utils.d.ts.map +1 -0
- package/lib/common/fuzzy-match-utils.js +96 -0
- package/lib/common/fuzzy-match-utils.js.map +1 -0
- package/lib/common/fuzzy-match-utils.spec.d.ts +2 -0
- package/lib/common/fuzzy-match-utils.spec.d.ts.map +1 -0
- package/lib/common/fuzzy-match-utils.spec.js +109 -0
- package/lib/common/fuzzy-match-utils.spec.js.map +1 -0
- package/lib/common/fuzzy-search.d.ts +63 -0
- package/lib/common/fuzzy-search.d.ts.map +1 -0
- package/lib/common/fuzzy-search.js +101 -0
- package/lib/common/fuzzy-search.js.map +1 -0
- package/lib/common/fuzzy-search.spec.d.ts.map +1 -0
- package/lib/{browser/tree → common}/fuzzy-search.spec.js +20 -1
- package/lib/common/fuzzy-search.spec.js.map +1 -0
- package/lib/common/quick-pick-service.d.ts.map +1 -1
- package/lib/common/quick-pick-service.js +34 -7
- package/lib/common/quick-pick-service.js.map +1 -1
- package/lib/electron-browser/window/electron-window-service.d.ts +3 -2
- package/lib/electron-browser/window/electron-window-service.d.ts.map +1 -1
- package/lib/electron-browser/window/electron-window-service.js +5 -2
- package/lib/electron-browser/window/electron-window-service.js.map +1 -1
- package/lib/electron-common/electron-main-window-service.d.ts +2 -1
- package/lib/electron-common/electron-main-window-service.d.ts.map +1 -1
- package/lib/electron-main/electron-main-application.d.ts +1 -0
- package/lib/electron-main/electron-main-application.d.ts.map +1 -1
- package/lib/electron-main/electron-main-application.js +6 -0
- package/lib/electron-main/electron-main-application.js.map +1 -1
- package/lib/electron-main/electron-main-window-service-impl.d.ts +2 -1
- package/lib/electron-main/electron-main-window-service-impl.d.ts.map +1 -1
- package/lib/electron-main/electron-main-window-service-impl.js +6 -2
- package/lib/electron-main/electron-main-window-service-impl.js.map +1 -1
- package/package.json +4 -4
- package/src/browser/quick-input/quick-input-service.spec.ts +58 -9
- package/src/browser/tree/fuzzy-search.ts +2 -120
- package/src/browser/window/default-window-service.ts +6 -1
- package/src/browser/window/test/mock-window-service.ts +2 -1
- package/src/browser/window/window-service.ts +9 -1
- package/src/common/fuzzy-match-utils.spec.ts +141 -0
- package/src/common/fuzzy-match-utils.ts +78 -0
- package/src/{browser/tree → common}/fuzzy-search.spec.ts +23 -1
- package/src/common/fuzzy-search.ts +158 -0
- package/src/common/quick-pick-service.ts +44 -9
- package/src/electron-browser/window/electron-window-service.ts +7 -4
- package/src/electron-common/electron-main-window-service.ts +2 -1
- package/src/electron-main/electron-main-application.ts +7 -0
- package/src/electron-main/electron-main-window-service-impl.ts +7 -2
- package/lib/browser/tree/fuzzy-search.spec.d.ts.map +0 -1
- package/lib/browser/tree/fuzzy-search.spec.js.map +0 -1
- /package/lib/{browser/tree → common}/fuzzy-search.spec.d.ts +0 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 EclipseSource and others.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Returns the index of the first case-insensitive substring match of `pattern` in `text`,
|
|
19
|
+
* or `-1` if `pattern` is not a substring. Returns `0` for an empty pattern.
|
|
20
|
+
*/
|
|
21
|
+
export function findSubstringIndex(text: string, pattern: string): number {
|
|
22
|
+
if (!pattern) { return 0; }
|
|
23
|
+
return text.toLowerCase().indexOf(pattern.toLowerCase());
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function hasSubstringMatch(text: string, pattern: string): boolean {
|
|
27
|
+
return findSubstringIndex(text, pattern) !== -1;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const SEGMENT_SEPARATOR = /[\p{P}\s]+/u;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Tests whether `pattern` is a "prefix match" for `text`, accounting for punctuation-separated segments.
|
|
34
|
+
*
|
|
35
|
+
* The pattern is split on punctuation into query parts. The text is split on punctuation into segments.
|
|
36
|
+
* It is a prefix match when the first query part matches the start of segment 0, and each subsequent
|
|
37
|
+
* query part matches the start of a later segment, in order.
|
|
38
|
+
*
|
|
39
|
+
* Examples:
|
|
40
|
+
* - `hasPrefixMatch("workspace-server", "works-ser")` → true
|
|
41
|
+
* - `hasPrefixMatch("backend-workspace-service", "works-ser")` → false (first segment doesn't match)
|
|
42
|
+
* - `hasPrefixMatch("fontSize", "font")` → true (single-part prefix)
|
|
43
|
+
*/
|
|
44
|
+
export function hasPrefixMatch(text: string, pattern: string): boolean {
|
|
45
|
+
if (!pattern) { return true; }
|
|
46
|
+
const queryParts = pattern.toLowerCase().split(SEGMENT_SEPARATOR).filter(Boolean);
|
|
47
|
+
if (queryParts.length === 0) { return true; }
|
|
48
|
+
const textSegments = text.toLowerCase().split(SEGMENT_SEPARATOR).filter(Boolean);
|
|
49
|
+
if (textSegments.length === 0) { return false; }
|
|
50
|
+
// First query part must match the start of the first text segment.
|
|
51
|
+
if (!textSegments[0].startsWith(queryParts[0])) { return false; }
|
|
52
|
+
let segIdx = 1;
|
|
53
|
+
for (let qIdx = 1; qIdx < queryParts.length; qIdx++) {
|
|
54
|
+
let found = false;
|
|
55
|
+
while (segIdx < textSegments.length) {
|
|
56
|
+
if (textSegments[segIdx].startsWith(queryParts[qIdx])) {
|
|
57
|
+
segIdx++;
|
|
58
|
+
found = true;
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
segIdx++;
|
|
62
|
+
}
|
|
63
|
+
if (!found) { return false; }
|
|
64
|
+
}
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Returns a numeric rank for how well `pattern` matches `text`:
|
|
70
|
+
* - 0: prefix match (best)
|
|
71
|
+
* - 1: substring match
|
|
72
|
+
* - 2: fuzzy-only match (worst)
|
|
73
|
+
*/
|
|
74
|
+
export function matchRank(text: string, pattern: string): number {
|
|
75
|
+
if (hasPrefixMatch(text, pattern)) { return 0; }
|
|
76
|
+
if (hasSubstringMatch(text, pattern)) { return 1; }
|
|
77
|
+
return 2;
|
|
78
|
+
}
|
|
@@ -71,7 +71,9 @@ describe('fuzzy-search', () => {
|
|
|
71
71
|
});
|
|
72
72
|
|
|
73
73
|
([
|
|
74
|
-
|
|
74
|
+
// "con" prefix matches (configs, CONTRIBUTING.MD) first, then substring matches (base.tsconfig.json, tsconfig.json), then fuzzy-only (base.nyc.json)
|
|
75
|
+
['con', ['configs', 'base.tsconfig.json', 'tsconfig.json', 'base.nyc.json', 'CONTRIBUTING.MD'],
|
|
76
|
+
['configs', 'CONTRIBUTING.MD', 'base.tsconfig.json', 'tsconfig.json', 'base.nyc.json']],
|
|
75
77
|
['bcn', ['baconing', 'narwhal', 'a mighty bear canoe'], ['baconing', 'a mighty bear canoe']]
|
|
76
78
|
] as ([string, string[], string[]])[]).forEach(test => {
|
|
77
79
|
const [pattern, items, expected] = test;
|
|
@@ -80,6 +82,26 @@ describe('fuzzy-search', () => {
|
|
|
80
82
|
});
|
|
81
83
|
});
|
|
82
84
|
|
|
85
|
+
it('should rank substring matches before fuzzy-only matches', async () => {
|
|
86
|
+
const results = await search('font', ['reformatting', 'fontSize', 'fontFamily']);
|
|
87
|
+
// "reformatting" contains f-o-n-t scattered but not as substring; fontSize/fontFamily do
|
|
88
|
+
expectOrder(results, ['fontSize', 'fontFamily']);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should preserve original order for equal-score fuzzy-only matches', async () => {
|
|
92
|
+
// Both are fuzzy-only matches for "bcn" with different scores from the fuzzy library;
|
|
93
|
+
// when scores are equal, original array order (index) should be preserved.
|
|
94
|
+
const results = await search('ab', ['xab', 'yab']);
|
|
95
|
+
expectOrder(results, ['xab', 'yab']);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should highlight contiguous substring range instead of scattered fuzzy ranges', async () => {
|
|
99
|
+
const results = await search('works', ['browser-only-workspace-server.ts']);
|
|
100
|
+
expect(results).to.have.length(1);
|
|
101
|
+
// "works" appears at index 13 in "browser-only-workspace-server.ts"
|
|
102
|
+
expect(results[0].ranges).to.deep.equal([{ offset: 13, length: 5 }]);
|
|
103
|
+
});
|
|
104
|
+
|
|
83
105
|
function expectOrder(actual: FuzzySearch.Match<string>[], expected: string[]): void {
|
|
84
106
|
expect(actual.map(result => result.item)).to.be.deep.equal(expected);
|
|
85
107
|
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2018 TypeFox and others.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import * as fuzzy from 'fuzzy';
|
|
18
|
+
import { injectable } from 'inversify';
|
|
19
|
+
import { findSubstringIndex, matchRank } from './fuzzy-match-utils';
|
|
20
|
+
|
|
21
|
+
@injectable()
|
|
22
|
+
export class FuzzySearch {
|
|
23
|
+
|
|
24
|
+
private static readonly PRE = '\x01';
|
|
25
|
+
private static readonly POST = '\x02';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Filters the input and returns with an array that contains all items that match the pattern.
|
|
29
|
+
*/
|
|
30
|
+
async filter<T>(input: FuzzySearch.Input<T>): Promise<FuzzySearch.Match<T>[]> {
|
|
31
|
+
return fuzzy.filter(input.pattern, input.items.slice(), {
|
|
32
|
+
pre: FuzzySearch.PRE,
|
|
33
|
+
post: FuzzySearch.POST,
|
|
34
|
+
extract: input.transform
|
|
35
|
+
}).sort((left, right) => this.sortResultsForInput(left, right, input))
|
|
36
|
+
.map(result => this.mapResultForInput(result, input));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
protected sortResultsForInput<T>(left: fuzzy.FilterResult<T>, right: fuzzy.FilterResult<T>, input: FuzzySearch.Input<T>): number {
|
|
40
|
+
const leftRank = matchRank(input.transform(left.original), input.pattern);
|
|
41
|
+
const rightRank = matchRank(input.transform(right.original), input.pattern);
|
|
42
|
+
if (leftRank !== rightRank) { return leftRank - rightRank; }
|
|
43
|
+
return this.sortResults(left, right);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
protected sortResults<T>(left: fuzzy.FilterResult<T>, right: fuzzy.FilterResult<T>): number {
|
|
47
|
+
if (right.score !== left.score) { return right.score - left.score; }
|
|
48
|
+
return left.index - right.index;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
protected mapResultForInput<T>(result: fuzzy.FilterResult<T>, input: FuzzySearch.Input<T>): FuzzySearch.Match<T> {
|
|
52
|
+
const text = input.transform(result.original);
|
|
53
|
+
const substringIndex = input.pattern ? findSubstringIndex(text, input.pattern) : -1;
|
|
54
|
+
if (substringIndex !== -1) {
|
|
55
|
+
return {
|
|
56
|
+
item: result.original,
|
|
57
|
+
ranges: [{ offset: substringIndex, length: input.pattern.length }]
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
return this.mapResult(result);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
protected mapResult<T>(result: fuzzy.FilterResult<T>): FuzzySearch.Match<T> {
|
|
64
|
+
return {
|
|
65
|
+
item: result.original,
|
|
66
|
+
ranges: this.mapRanges(result.string)
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
protected mapRanges(input: string): ReadonlyArray<FuzzySearch.Range> {
|
|
71
|
+
const copy = input.split('').filter(s => s !== '');
|
|
72
|
+
const ranges: FuzzySearch.Range[] = [];
|
|
73
|
+
const validate = (pre: number, post: number) => {
|
|
74
|
+
if (preIndex > postIndex || (preIndex === -1) !== (postIndex === -1)) {
|
|
75
|
+
throw new Error(`Error when trying to map ranges. Escaped string was: '${input}. [${[...input].join('|')}]'`);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
let preIndex = copy.indexOf(FuzzySearch.PRE);
|
|
79
|
+
let postIndex = copy.indexOf(FuzzySearch.POST);
|
|
80
|
+
validate(preIndex, postIndex);
|
|
81
|
+
while (preIndex !== -1 && postIndex !== -1) {
|
|
82
|
+
ranges.push({
|
|
83
|
+
offset: preIndex,
|
|
84
|
+
length: postIndex - preIndex - 1
|
|
85
|
+
});
|
|
86
|
+
copy.splice(postIndex, 1);
|
|
87
|
+
copy.splice(preIndex, 1);
|
|
88
|
+
preIndex = copy.indexOf(FuzzySearch.PRE);
|
|
89
|
+
postIndex = copy.indexOf(FuzzySearch.POST);
|
|
90
|
+
}
|
|
91
|
+
if (ranges.length === 0) {
|
|
92
|
+
throw new Error(`Unexpected zero ranges for match-string: ${input}.`);
|
|
93
|
+
}
|
|
94
|
+
return ranges;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Fuzzy searcher.
|
|
101
|
+
*/
|
|
102
|
+
export namespace FuzzySearch {
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* A range representing the match region.
|
|
106
|
+
*/
|
|
107
|
+
export interface Range {
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* The zero based offset of the match region.
|
|
111
|
+
*/
|
|
112
|
+
readonly offset: number;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* The length of the match region.
|
|
116
|
+
*/
|
|
117
|
+
readonly length: number;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* A fuzzy search match.
|
|
122
|
+
*/
|
|
123
|
+
export interface Match<T> {
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* The original item.
|
|
127
|
+
*/
|
|
128
|
+
readonly item: T;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* An array of ranges representing the match regions.
|
|
132
|
+
*/
|
|
133
|
+
readonly ranges: ReadonlyArray<Range>;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* The fuzzy search input.
|
|
138
|
+
*/
|
|
139
|
+
export interface Input<T> {
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* The pattern to match.
|
|
143
|
+
*/
|
|
144
|
+
readonly pattern: string;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* The items to filter based on the `pattern`.
|
|
148
|
+
*/
|
|
149
|
+
readonly items: ReadonlyArray<T>;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Function that extracts the string from the inputs which will be used to evaluate the fuzzy matching filter.
|
|
153
|
+
*/
|
|
154
|
+
readonly transform: (item: T) => string;
|
|
155
|
+
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
}
|
|
@@ -19,6 +19,7 @@ import { Event } from './event';
|
|
|
19
19
|
import { KeySequence } from './keys';
|
|
20
20
|
import { CancellationToken } from './cancellation';
|
|
21
21
|
import { Severity } from './severity';
|
|
22
|
+
import { findSubstringIndex, matchRank } from './fuzzy-match-utils';
|
|
22
23
|
|
|
23
24
|
export const quickPickServicePath = '/services/quickPick';
|
|
24
25
|
export const QuickPickService = Symbol('QuickPickService');
|
|
@@ -303,24 +304,52 @@ export function filterItems(items: QuickPickItemOrSeparator[], filter: string):
|
|
|
303
304
|
return items;
|
|
304
305
|
}
|
|
305
306
|
|
|
306
|
-
|
|
307
|
+
function matchesFilter(item: QuickPickItem): boolean {
|
|
308
|
+
return fuzzy.test(filter, item.label) ||
|
|
309
|
+
(!!item.description && fuzzy.test(filter, item.description)) ||
|
|
310
|
+
(!!item.detail && fuzzy.test(filter, item.detail));
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function itemMatchRank(item: QuickPickItem): number {
|
|
314
|
+
return Math.min(
|
|
315
|
+
matchRank(item.label, filter),
|
|
316
|
+
item.description ? matchRank(item.description, filter) : 2,
|
|
317
|
+
item.detail ? matchRank(item.detail, filter) : 2
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Process items in separator groups, sorted by match rank within each group.
|
|
322
|
+
const result: QuickPickItemOrSeparator[] = [];
|
|
323
|
+
let currentSeparator: QuickPickSeparator | undefined;
|
|
324
|
+
let groupMatches: { item: QuickPickItem; rank: number }[] = [];
|
|
325
|
+
|
|
326
|
+
const flushGroup = (): void => {
|
|
327
|
+
if (groupMatches.length > 0) {
|
|
328
|
+
if (currentSeparator) {
|
|
329
|
+
result.push(currentSeparator);
|
|
330
|
+
}
|
|
331
|
+
groupMatches.sort((a, b) => a.rank - b.rank);
|
|
332
|
+
result.push(...groupMatches.map(m => m.item));
|
|
333
|
+
}
|
|
334
|
+
groupMatches = [];
|
|
335
|
+
};
|
|
336
|
+
|
|
307
337
|
for (const item of items) {
|
|
308
338
|
if (item.type === 'separator') {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
(item.description && fuzzy.test(filter, item.description)) ||
|
|
313
|
-
(item.detail && fuzzy.test(filter, item.detail))
|
|
314
|
-
) {
|
|
339
|
+
flushGroup();
|
|
340
|
+
currentSeparator = item;
|
|
341
|
+
} else if (matchesFilter(item)) {
|
|
315
342
|
item.highlights = {
|
|
316
343
|
label: findMatches(item.label, filter),
|
|
317
344
|
description: item.description ? findMatches(item.description, filter) : undefined,
|
|
318
345
|
detail: item.detail ? findMatches(item.detail, filter) : undefined
|
|
319
346
|
};
|
|
320
|
-
|
|
347
|
+
groupMatches.push({ item, rank: itemMatchRank(item) });
|
|
321
348
|
}
|
|
322
349
|
}
|
|
323
|
-
|
|
350
|
+
flushGroup();
|
|
351
|
+
|
|
352
|
+
return result;
|
|
324
353
|
}
|
|
325
354
|
|
|
326
355
|
/**
|
|
@@ -337,6 +366,12 @@ export function findMatches(word: string, pattern: string): Array<{ start: numbe
|
|
|
337
366
|
return undefined;
|
|
338
367
|
}
|
|
339
368
|
|
|
369
|
+
// Prefer a contiguous substring highlight over scattered fuzzy character highlights.
|
|
370
|
+
const substringIndex = findSubstringIndex(word, pattern);
|
|
371
|
+
if (substringIndex !== -1) {
|
|
372
|
+
return [{ start: substringIndex, end: substringIndex + pattern.length }];
|
|
373
|
+
}
|
|
374
|
+
|
|
340
375
|
const delimiter = '\u0000'; // null byte that shouldn't appear in the input and is used to denote matches.
|
|
341
376
|
const matchResult = fuzzy.match(pattern.replace(/\u0000/gu, ''), word, { pre: delimiter, post: delimiter });
|
|
342
377
|
if (!matchResult) {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
|
|
17
17
|
import { injectable, inject, postConstruct } from 'inversify';
|
|
18
|
-
import { NewWindowOptions
|
|
18
|
+
import { NewWindowOptions } from '../../common/window';
|
|
19
19
|
import { DefaultWindowService } from '../../browser/window/default-window-service';
|
|
20
20
|
import { ElectronMainWindowService } from '../../electron-common/electron-main-window-service';
|
|
21
21
|
import { ElectronWindowPreferences } from '../../electron-common/electron-window-preferences';
|
|
@@ -57,8 +57,12 @@ export class ElectronWindowService extends DefaultWindowService {
|
|
|
57
57
|
return undefined;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
override openNewDefaultWindow(params?:
|
|
61
|
-
this.delegate.openNewDefaultWindow(params);
|
|
60
|
+
override async openNewDefaultWindow(params?: WindowReloadOptions): Promise<number> {
|
|
61
|
+
return this.delegate.openNewDefaultWindow(params?.search);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
override closeWindow(windowId: number): void {
|
|
65
|
+
this.delegate.closeWindow(windowId);
|
|
62
66
|
}
|
|
63
67
|
|
|
64
68
|
override focus(): void {
|
|
@@ -116,4 +120,3 @@ export class ElectronWindowService extends DefaultWindowService {
|
|
|
116
120
|
}
|
|
117
121
|
}
|
|
118
122
|
}
|
|
119
|
-
|
|
@@ -20,5 +20,6 @@ export const electronMainWindowServicePath = '/services/electron-window';
|
|
|
20
20
|
export const ElectronMainWindowService = Symbol('ElectronMainWindowService');
|
|
21
21
|
export interface ElectronMainWindowService {
|
|
22
22
|
openNewWindow(url: string, options?: NewWindowOptions): undefined;
|
|
23
|
-
openNewDefaultWindow(params?: WindowSearchParams):
|
|
23
|
+
openNewDefaultWindow(params?: WindowSearchParams): Promise<number>;
|
|
24
|
+
closeWindow(windowId: number): void;
|
|
24
25
|
}
|
|
@@ -502,6 +502,13 @@ export class ElectronMainApplication {
|
|
|
502
502
|
};
|
|
503
503
|
}
|
|
504
504
|
|
|
505
|
+
closeWindowById(webContentsId: number): void {
|
|
506
|
+
const window = this.windows.get(webContentsId);
|
|
507
|
+
if (window) {
|
|
508
|
+
window.close(StopReason.Close);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
505
512
|
async openDefaultWindow(params?: WindowSearchParams): Promise<BrowserWindow> {
|
|
506
513
|
const options = this.getDefaultTheiaWindowOptions();
|
|
507
514
|
const [uri, electronWindow] = await Promise.all([this.createWindowUri(params), this.reuseOrCreateWindow(options)]);
|
|
@@ -37,8 +37,13 @@ export class ElectronMainWindowServiceImpl implements ElectronMainWindowService
|
|
|
37
37
|
return undefined;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
openNewDefaultWindow(params?: WindowSearchParams):
|
|
41
|
-
this.app.openDefaultWindow(params);
|
|
40
|
+
async openNewDefaultWindow(params?: WindowSearchParams): Promise<number> {
|
|
41
|
+
const electronWindow = await this.app.openDefaultWindow(params);
|
|
42
|
+
return electronWindow.webContents.id;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
closeWindow(windowId: number): void {
|
|
46
|
+
this.app.closeWindowById(windowId);
|
|
42
47
|
}
|
|
43
48
|
|
|
44
49
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fuzzy-search.spec.d.ts","sourceRoot":"","sources":["../../../src/browser/tree/fuzzy-search.spec.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fuzzy-search.spec.js","sourceRoot":"","sources":["../../../src/browser/tree/fuzzy-search.spec.ts"],"names":[],"mappings":"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"}
|
|
File without changes
|