Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/preferences/common/preferences.ts
5284 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { raceTimeout } from '../../../../base/common/async.js';
7
import { CancellationToken } from '../../../../base/common/cancellation.js';
8
import { IStringDictionary } from '../../../../base/common/collections.js';
9
import { IExtensionRecommendations } from '../../../../base/common/product.js';
10
import { localize } from '../../../../nls.js';
11
import { RawContextKey } from '../../../../platform/contextkey/common/contextkey.js';
12
import { IExtensionGalleryService, IGalleryExtension } from '../../../../platform/extensionManagement/common/extensionManagement.js';
13
import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
14
import { IProductService } from '../../../../platform/product/common/productService.js';
15
import { IChatEntitlementService } from '../../../services/chat/common/chatEntitlementService.js';
16
import { ISearchResult, ISettingsEditorModel } from '../../../services/preferences/common/preferences.js';
17
18
export interface IWorkbenchSettingsConfiguration {
19
workbench: {
20
settings: {
21
openDefaultSettings: boolean;
22
naturalLanguageSearchEndpoint: string;
23
naturalLanguageSearchKey: string;
24
naturalLanguageSearchAutoIngestFeedback: boolean;
25
useNaturalLanguageSearchPost: boolean;
26
enableNaturalLanguageSearch: boolean;
27
enableNaturalLanguageSearchFeedback: boolean;
28
};
29
};
30
}
31
32
export interface IEndpointDetails {
33
urlBase: string;
34
key?: string;
35
}
36
37
export const IPreferencesSearchService = createDecorator<IPreferencesSearchService>('preferencesSearchService');
38
39
export interface IPreferencesSearchService {
40
readonly _serviceBrand: undefined;
41
42
getLocalSearchProvider(filter: string): ISearchProvider;
43
getRemoteSearchProvider(filter: string, newExtensionsOnly?: boolean): ISearchProvider | undefined;
44
getAiSearchProvider(filter: string): IAiSearchProvider | undefined;
45
}
46
47
export interface ISearchProvider {
48
searchModel(preferencesModel: ISettingsEditorModel, token: CancellationToken): Promise<ISearchResult | null>;
49
}
50
51
export interface IRemoteSearchProvider extends ISearchProvider {
52
setFilter(filter: string): void;
53
}
54
55
export interface IAiSearchProvider extends IRemoteSearchProvider {
56
getLLMRankedResults(token: CancellationToken): Promise<ISearchResult | null>;
57
}
58
59
export const PREFERENCES_EDITOR_COMMAND_OPEN = 'workbench.preferences.action.openPreferencesEditor';
60
export const CONTEXT_PREFERENCES_SEARCH_FOCUS = new RawContextKey<boolean>('inPreferencesSearch', false);
61
62
export const SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS = 'settings.action.clearSearchResults';
63
export const SETTINGS_EDITOR_COMMAND_SHOW_AI_RESULTS = 'settings.action.showAIResults';
64
export const SETTINGS_EDITOR_COMMAND_TOGGLE_AI_SEARCH = 'settings.action.toggleAiSearch';
65
export const SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU = 'settings.action.showContextMenu';
66
export const SETTINGS_EDITOR_COMMAND_SUGGEST_FILTERS = 'settings.action.suggestFilters';
67
68
export const CONTEXT_SETTINGS_EDITOR = new RawContextKey<boolean>('inSettingsEditor', false);
69
export const CONTEXT_SETTINGS_JSON_EDITOR = new RawContextKey<boolean>('inSettingsJSONEditor', false);
70
export const CONTEXT_SETTINGS_SEARCH_FOCUS = new RawContextKey<boolean>('inSettingsSearch', false);
71
export const CONTEXT_TOC_ROW_FOCUS = new RawContextKey<boolean>('settingsTocRowFocus', false);
72
export const CONTEXT_SETTINGS_ROW_FOCUS = new RawContextKey<boolean>('settingRowFocus', false);
73
export const CONTEXT_KEYBINDINGS_EDITOR = new RawContextKey<boolean>('inKeybindings', false);
74
export const CONTEXT_KEYBINDINGS_SEARCH_FOCUS = new RawContextKey<boolean>('inKeybindingsSearch', false);
75
export const CONTEXT_KEYBINDINGS_SEARCH_HAS_VALUE = new RawContextKey<boolean>('keybindingsSearchHasValue', false);
76
export const CONTEXT_KEYBINDING_FOCUS = new RawContextKey<boolean>('keybindingFocus', false);
77
export const CONTEXT_WHEN_FOCUS = new RawContextKey<boolean>('whenFocus', false);
78
export const CONTEXT_AI_SETTING_RESULTS_AVAILABLE = new RawContextKey<boolean>('aiSettingResultsAvailable', false);
79
80
export const KEYBINDINGS_EDITOR_COMMAND_SEARCH = 'keybindings.editor.searchKeybindings';
81
export const KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS = 'keybindings.editor.clearSearchResults';
82
export const KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_HISTORY = 'keybindings.editor.clearSearchHistory';
83
export const KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS = 'keybindings.editor.recordSearchKeys';
84
export const KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE = 'keybindings.editor.toggleSortByPrecedence';
85
export const KEYBINDINGS_EDITOR_COMMAND_DEFINE = 'keybindings.editor.defineKeybinding';
86
export const KEYBINDINGS_EDITOR_COMMAND_ADD = 'keybindings.editor.addKeybinding';
87
export const KEYBINDINGS_EDITOR_COMMAND_DEFINE_WHEN = 'keybindings.editor.defineWhenExpression';
88
export const KEYBINDINGS_EDITOR_COMMAND_ACCEPT_WHEN = 'keybindings.editor.acceptWhenExpression';
89
export const KEYBINDINGS_EDITOR_COMMAND_REJECT_WHEN = 'keybindings.editor.rejectWhenExpression';
90
export const KEYBINDINGS_EDITOR_COMMAND_REMOVE = 'keybindings.editor.removeKeybinding';
91
export const KEYBINDINGS_EDITOR_COMMAND_RESET = 'keybindings.editor.resetKeybinding';
92
export const KEYBINDINGS_EDITOR_COMMAND_COPY = 'keybindings.editor.copyKeybindingEntry';
93
export const KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND = 'keybindings.editor.copyCommandKeybindingEntry';
94
export const KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND_TITLE = 'keybindings.editor.copyCommandTitle';
95
export const KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR = 'keybindings.editor.showConflicts';
96
export const KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS = 'keybindings.editor.focusKeybindings';
97
export const KEYBINDINGS_EDITOR_SHOW_DEFAULT_KEYBINDINGS = 'keybindings.editor.showDefaultKeybindings';
98
export const KEYBINDINGS_EDITOR_SHOW_USER_KEYBINDINGS = 'keybindings.editor.showUserKeybindings';
99
export const KEYBINDINGS_EDITOR_SHOW_EXTENSION_KEYBINDINGS = 'keybindings.editor.showExtensionKeybindings';
100
101
export const MODIFIED_SETTING_TAG = 'modified';
102
export const EXTENSION_SETTING_TAG = 'ext:';
103
export const FEATURE_SETTING_TAG = 'feature:';
104
export const ID_SETTING_TAG = 'id:';
105
export const LANGUAGE_SETTING_TAG = 'lang:';
106
export const GENERAL_TAG_SETTING_TAG = 'tag:';
107
export const POLICY_SETTING_TAG = 'hasPolicy';
108
export const WORKSPACE_TRUST_SETTING_TAG = 'workspaceTrust';
109
export const REQUIRE_TRUSTED_WORKSPACE_SETTING_TAG = 'requireTrustedWorkspace';
110
export const ADVANCED_SETTING_TAG = 'advanced';
111
export const KEYBOARD_LAYOUT_OPEN_PICKER = 'workbench.action.openKeyboardLayoutPicker';
112
113
export const ENABLE_LANGUAGE_FILTER = true;
114
115
export const ENABLE_EXTENSION_TOGGLE_SETTINGS = true;
116
export const EXTENSION_FETCH_TIMEOUT_MS = 1000;
117
118
export const STRING_MATCH_SEARCH_PROVIDER_NAME = 'local';
119
export const TF_IDF_SEARCH_PROVIDER_NAME = 'tfIdf';
120
export const FILTER_MODEL_SEARCH_PROVIDER_NAME = 'filterModel';
121
export const EMBEDDINGS_SEARCH_PROVIDER_NAME = 'embeddingsFull';
122
export const LLM_RANKED_SEARCH_PROVIDER_NAME = 'llmRanked';
123
124
export enum WorkbenchSettingsEditorSettings {
125
ShowAISearchToggle = 'workbench.settings.showAISearchToggle',
126
EnableNaturalLanguageSearch = 'workbench.settings.enableNaturalLanguageSearch',
127
}
128
129
export type ExtensionToggleData = {
130
settingsEditorRecommendedExtensions: IStringDictionary<IExtensionRecommendations>;
131
recommendedExtensionsGalleryInfo: IStringDictionary<IGalleryExtension>;
132
};
133
134
let cachedExtensionToggleData: ExtensionToggleData | undefined;
135
136
export async function getExperimentalExtensionToggleData(
137
chatEntitlementService: IChatEntitlementService,
138
extensionGalleryService: IExtensionGalleryService,
139
productService: IProductService,
140
): Promise<ExtensionToggleData | undefined> {
141
if (!ENABLE_EXTENSION_TOGGLE_SETTINGS) {
142
return undefined;
143
}
144
145
if (!extensionGalleryService.isEnabled()) {
146
return undefined;
147
}
148
149
if (chatEntitlementService.sentiment.hidden || chatEntitlementService.sentiment.disabled) {
150
return undefined;
151
}
152
153
if (cachedExtensionToggleData) {
154
return cachedExtensionToggleData;
155
}
156
157
if (productService.extensionRecommendations) {
158
const settingsEditorRecommendedExtensions: IStringDictionary<IExtensionRecommendations> = {};
159
Object.keys(productService.extensionRecommendations).forEach(extensionId => {
160
const extensionInfo = productService.extensionRecommendations![extensionId];
161
if (extensionInfo.onSettingsEditorOpen) {
162
settingsEditorRecommendedExtensions[extensionId] = extensionInfo;
163
}
164
});
165
166
const recommendedExtensionsGalleryInfo: IStringDictionary<IGalleryExtension> = {};
167
for (const key in settingsEditorRecommendedExtensions) {
168
const extensionId = key;
169
// Recommend prerelease if not on Stable.
170
const isStable = productService.quality === 'stable';
171
try {
172
const extensions = await raceTimeout(
173
extensionGalleryService.getExtensions([{ id: extensionId, preRelease: !isStable }], CancellationToken.None),
174
EXTENSION_FETCH_TIMEOUT_MS);
175
if (extensions?.length === 1) {
176
recommendedExtensionsGalleryInfo[key] = extensions[0];
177
} else {
178
// same as network connection fail. we do not want a blank settings page: https://github.com/microsoft/vscode/issues/195722
179
// so instead of returning partial data we return undefined here
180
return undefined;
181
}
182
} catch (e) {
183
// Network connection fail. Return nothing rather than partial data.
184
return undefined;
185
}
186
}
187
188
cachedExtensionToggleData = {
189
settingsEditorRecommendedExtensions,
190
recommendedExtensionsGalleryInfo
191
};
192
return cachedExtensionToggleData;
193
}
194
return undefined;
195
}
196
197
/**
198
* Compares two nullable numbers such that null values always come after defined ones.
199
*/
200
export function compareTwoNullableNumbers(a: number | undefined, b: number | undefined): number {
201
const aOrMax = a ?? Number.MAX_SAFE_INTEGER;
202
const bOrMax = b ?? Number.MAX_SAFE_INTEGER;
203
if (aOrMax < bOrMax) {
204
return -1;
205
} else if (aOrMax > bOrMax) {
206
return 1;
207
} else {
208
return 0;
209
}
210
}
211
212
export const PREVIEW_INDICATOR_DESCRIPTION = localize('previewIndicatorDescription', "Preview setting: this setting controls a new feature that is still under refinement yet ready to use. Feedback is welcome.");
213
export const EXPERIMENTAL_INDICATOR_DESCRIPTION = localize('experimentalIndicatorDescription', "Experimental setting: this setting controls a new feature that is actively being developed and may be unstable. It is subject to change or removal.");
214
export const ADVANCED_INDICATOR_DESCRIPTION = localize('advancedIndicatorDescription', "Advanced setting: this setting is intended for advanced scenarios and configurations. Only modify this if you know what it does.");
215
216
export const knownAcronyms = new Set<string>();
217
[
218
'css',
219
'html',
220
'scss',
221
'less',
222
'json',
223
'js',
224
'ts',
225
'ie',
226
'id',
227
'php',
228
'scm',
229
].forEach(str => knownAcronyms.add(str));
230
231
export const knownTermMappings = new Map<string, string>();
232
knownTermMappings.set('power shell', 'PowerShell');
233
knownTermMappings.set('powershell', 'PowerShell');
234
knownTermMappings.set('javascript', 'JavaScript');
235
knownTermMappings.set('typescript', 'TypeScript');
236
knownTermMappings.set('github', 'GitHub');
237
knownTermMappings.set('jet brains', 'JetBrains');
238
knownTermMappings.set('jetbrains', 'JetBrains');
239
knownTermMappings.set('re sharper', 'ReSharper');
240
knownTermMappings.set('resharper', 'ReSharper');
241
242
export function wordifyKey(key: string): string {
243
key = key
244
.replace(/\.([a-z0-9])/g, (_, p1) => ` \u203A ${p1.toUpperCase()}`) // Replace dot with spaced '>'
245
.replace(/([a-z0-9])([A-Z])/g, '$1 $2') // Camel case to spacing, fooBar => foo Bar
246
.replace(/([A-Z]{1,})([A-Z][a-z])/g, '$1 $2') // Split consecutive capitals letters, AISearch => AI Search
247
.replace(/^[a-z]/g, match => match.toUpperCase()) // Upper casing all first letters, foo => Foo
248
.replace(/\b\w+\b/g, match => { // Upper casing known acronyms
249
return knownAcronyms.has(match.toLowerCase()) ?
250
match.toUpperCase() :
251
match;
252
});
253
254
for (const [k, v] of knownTermMappings) {
255
key = key.replace(new RegExp(`\\b${k}\\b`, 'gi'), v);
256
}
257
258
return key;
259
}
260
261