Skip to content

Commit 80e6ad4

Browse files
committed
support muli-schemas selection in schema picker
Signed-off-by: Morgan Chang <shin19991207@gmail.com>
1 parent f35420f commit 80e6ad4

2 files changed

Lines changed: 433 additions & 71 deletions

File tree

src/schema-status-bar-item.ts

Lines changed: 85 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
TextEditor,
1111
StatusBarItem,
1212
QuickPickItem,
13-
ThemeColor,
13+
ThemeIcon,
1414
workspace,
1515
Uri,
1616
} from 'vscode';
@@ -49,10 +49,12 @@ const getSchemaRequestType: RequestType<FileUri, JSONSchema[], {}> = new Request
4949
export let statusBarItem: StatusBarItem;
5050

5151
let client: CommonLanguageClient;
52-
let versionSelection: SchemaItem = undefined;
5352

54-
const selectVersionLabel = 'Select Different Version';
5553
const noSchemaLabel = 'No JSON Schema';
54+
const selectSchemaVersionButton = {
55+
iconPath: new ThemeIcon('versions'),
56+
tooltip: 'Select schema version',
57+
};
5658

5759
export function createJSONSchemaStatusBarItem(context: ExtensionContext, languageclient: CommonLanguageClient): void {
5860
if (statusBarItem) {
@@ -69,45 +71,49 @@ export function createJSONSchemaStatusBarItem(context: ExtensionContext, languag
6971
context.subscriptions.push(statusBarItem);
7072

7173
context.subscriptions.push(window.onDidChangeActiveTextEditor(updateStatusBar));
74+
context.subscriptions.push(
75+
workspace.onDidChangeTextDocument((event) => {
76+
const editor = window.activeTextEditor;
77+
if (
78+
editor?.document.languageId === 'yaml' &&
79+
editor.document.uri.toString() === event.document.uri.toString() &&
80+
event.contentChanges?.some((change) => /#\s*yaml-language-server:\s*\$schema=/.test(change.text))
81+
) {
82+
updateStatusBar(editor);
83+
}
84+
})
85+
);
7286

7387
updateStatusBar(window.activeTextEditor);
7488
}
7589

7690
async function updateStatusBar(editor: TextEditor): Promise<void> {
7791
if (editor && editor.document.languageId === 'yaml') {
78-
versionSelection = undefined;
7992
const fileUri = editor.document.uri.toString();
8093
// get schema info there
8194
const schema = await client.sendRequest(getSchemaRequestType, fileUri);
8295
if (!schema || schema.length === 0) {
8396
statusBarItem.text = noSchemaLabel;
84-
statusBarItem.tooltip = 'Select JSON Schema';
85-
statusBarItem.backgroundColor = undefined;
97+
statusBarItem.tooltip = 'Select JSON Schemas';
8698
} else if (schema.length === 1) {
99+
statusBarItem.tooltip = 'Select JSON Schemas';
87100
statusBarItem.text = schema[0].name ?? schema[0].uri;
88101
let version;
89102
if (schema[0].versions) {
90103
version = findUsedVersion(schema[0].versions, schema[0].uri);
91104
} else {
92105
const schemas = await client.sendRequest(getJSONSchemasRequestType, fileUri);
93-
let versionSchema: JSONSchema;
94106
const schemaStoreItem = findSchemaStoreItem(schemas, schema[0].uri);
95107
if (schemaStoreItem) {
96-
[version, versionSchema] = schemaStoreItem;
97-
(versionSchema as MatchingJSONSchema).usedForCurrentFile = true;
98-
versionSchema.uri = schema[0].uri;
99-
versionSelection = createSelectVersionItem(version, versionSchema as MatchingJSONSchema);
108+
version = schemaStoreItem[0];
100109
}
101110
}
102111
if (version && !statusBarItem.text.includes(version)) {
103112
statusBarItem.text += `(${version})`;
104113
}
105-
statusBarItem.tooltip = 'Select JSON Schema';
106-
statusBarItem.backgroundColor = undefined;
107114
} else {
108-
statusBarItem.text = 'Multiple JSON Schemas...';
109-
statusBarItem.tooltip = 'Multiple JSON Schema used to validate this file, click to select one';
110-
statusBarItem.backgroundColor = new ThemeColor('statusBarItem.warningBackground');
115+
statusBarItem.text = 'Multiple JSON Schemas';
116+
statusBarItem.tooltip = `Validated using ${schema.length} JSON schemas:\n${schema.map((s) => s.name ?? s.uri).join('\n')}`;
111117
}
112118

113119
statusBarItem.show();
@@ -123,22 +129,16 @@ async function showSchemaSelection(): Promise<void> {
123129
let pickItems: SchemaItem[] = [];
124130

125131
for (const val of schemas) {
126-
if (val.usedForCurrentFile && val.versions) {
127-
const item = createSelectVersionItem(findUsedVersion(val.versions, val.uri), val);
128-
pickItems.unshift(item);
129-
}
130132
const item = {
131133
label: val.name ?? val.uri,
132134
description: val.description,
133135
detail: val.usedForCurrentFile ? 'Used for current file$(check)' : '',
134136
alwaysShow: val.usedForCurrentFile,
137+
buttons: val.versions ? [selectSchemaVersionButton] : undefined,
135138
schema: val,
136139
};
137140
pickItems.push(item);
138141
}
139-
if (versionSelection) {
140-
pickItems.unshift(versionSelection);
141-
}
142142

143143
pickItems = pickItems.sort((a, b) => {
144144
if (a.schema?.usedForCurrentFile && a.schema?.versions) {
@@ -156,26 +156,39 @@ async function showSchemaSelection(): Promise<void> {
156156
return a.label.localeCompare(b.label);
157157
});
158158

159-
pickItems.unshift({
159+
const noSchemaItem: SchemaItem = {
160160
label: noSchemaLabel,
161161
alwaysShow: true,
162162
disableSchemaDetection: true,
163-
});
163+
};
164+
pickItems.unshift(noSchemaItem);
164165

165166
schemasPick.items = pickItems;
167+
schemasPick.canSelectMany = true;
168+
const selectedSchemaItems = pickItems.filter((item) => item.schema?.usedForCurrentFile);
169+
schemasPick.selectedItems =
170+
selectedSchemaItems.length > 0 && !isSchemaDetectionDisabled(fileUri) ? selectedSchemaItems : [noSchemaItem];
166171
schemasPick.placeholder = 'Search JSON schema';
167-
schemasPick.title = 'Select JSON schema';
172+
schemasPick.title = 'Select JSON schemas';
168173
schemasPick.onDidHide(() => schemasPick.dispose());
174+
schemasPick.onDidTriggerItemButton((event) => {
175+
if (event.button === selectSchemaVersionButton && event.item.schema?.versions) {
176+
const selectedSchemaUris = schemasPick.selectedItems.flatMap((item) => (item.schema ? [item.schema.uri] : []));
177+
schemasPick.hide();
178+
handleSchemaVersionSelection(event.item.schema, fileUri, selectedSchemaUris);
179+
}
180+
});
169181

170-
schemasPick.onDidChangeSelection((selection) => {
182+
schemasPick.onDidAccept(async () => {
171183
try {
172-
if (selection.length > 0) {
173-
if (selection[0].label === selectVersionLabel) {
174-
handleSchemaVersionSelection(selection[0].schema, fileUri);
175-
} else if (selection[0].disableSchemaDetection) {
176-
writeDisableSchemaDetectionMapping(fileUri);
177-
} else if (selection[0].schema) {
178-
writeSchemaUriMapping(selection[0].schema.uri, fileUri);
184+
if (schemasPick.selectedItems.length === 0 || schemasPick.selectedItems.some((item) => item.disableSchemaDetection)) {
185+
await writeDisableSchemaDetectionMapping(fileUri);
186+
} else {
187+
const schemaUrls = schemasPick.selectedItems.flatMap((item) => (item.schema ? [item.schema.uri] : []));
188+
if (schemaUrls.length === 0) {
189+
await writeDisableSchemaDetectionMapping(fileUri);
190+
} else {
191+
await writeSchemaUriMappings(schemaUrls, fileUri);
179192
}
180193
}
181194
} catch (err) {
@@ -200,6 +213,15 @@ function getFilePatternCandidates(fileUri: string): string[] {
200213
return Array.from(candidates).filter(Boolean);
201214
}
202215

216+
function isSchemaDetectionDisabled(fileUri: string): boolean {
217+
const disableSchemaDetection = workspace.getConfiguration('yaml').get('disableSchemaDetection');
218+
const filePatterns = getFilePatternCandidates(fileUri);
219+
if (Array.isArray(disableSchemaDetection)) {
220+
return disableSchemaDetection.some((value) => typeof value === 'string' && filePatterns.includes(value));
221+
}
222+
return typeof disableSchemaDetection === 'string' && filePatterns.includes(disableSchemaDetection);
223+
}
224+
203225
function deleteExistingFilePattern(settings: Record<string, unknown>, filePatterns: string[]): unknown {
204226
for (const key in settings) {
205227
if (Object.prototype.hasOwnProperty.call(settings, key)) {
@@ -253,14 +275,6 @@ function addFilePatternToSetting(setting: unknown, fileUri: string): string | st
253275
return [fileUri];
254276
}
255277

256-
function createSelectVersionItem(version: string, schema: MatchingJSONSchema): SchemaItem {
257-
return {
258-
label: selectVersionLabel,
259-
detail: `Current: ${version}`,
260-
alwaysShow: true,
261-
schema: schema,
262-
};
263-
}
264278
function findSchemaStoreItem(schemas: JSONSchema[], url: string): [string, JSONSchema] | undefined {
265279
for (const schema of schemas) {
266280
if (schema.versions) {
@@ -273,34 +287,41 @@ function findSchemaStoreItem(schemas: JSONSchema[], url: string): [string, JSONS
273287
}
274288
}
275289

276-
function writeSchemaUriMapping(schemaUrl: string, fileUri: string): void {
290+
async function writeSchemaUriMappings(schemaUrls: string[], fileUri: string): Promise<void> {
277291
const yamlConfiguration = workspace.getConfiguration('yaml');
278292
const settings: Record<string, unknown> = yamlConfiguration.get('schemas');
279293
const disableSchemaDetection = yamlConfiguration.get('disableSchemaDetection');
280294
const filePatterns = getFilePatternCandidates(fileUri);
281-
yamlConfiguration.update('disableSchemaDetection', removeFilePatternFromSetting(disableSchemaDetection, filePatterns));
295+
await yamlConfiguration.update('disableSchemaDetection', removeFilePatternFromSetting(disableSchemaDetection, filePatterns));
282296
const newSettings = Object.assign({}, settings);
283297
deleteExistingFilePattern(newSettings, filePatterns);
284-
const schemaSettings = newSettings[schemaUrl];
285-
if (schemaSettings) {
286-
if (Array.isArray(schemaSettings)) {
287-
(schemaSettings as Array<string>).push(fileUri);
288-
} else if (typeof schemaSettings === 'string') {
289-
newSettings[schemaUrl] = [schemaSettings, fileUri];
298+
299+
for (const schemaUrl of schemaUrls) {
300+
const schemaSettings = newSettings[schemaUrl];
301+
if (schemaSettings) {
302+
if (Array.isArray(schemaSettings)) {
303+
const schemaFilePatterns = schemaSettings.filter((value): value is string => typeof value === 'string');
304+
if (!schemaFilePatterns.includes(fileUri)) {
305+
schemaFilePatterns.push(fileUri);
306+
}
307+
newSettings[schemaUrl] = schemaFilePatterns;
308+
} else if (typeof schemaSettings === 'string') {
309+
newSettings[schemaUrl] = schemaSettings === fileUri ? schemaSettings : [schemaSettings, fileUri];
310+
}
311+
} else {
312+
newSettings[schemaUrl] = fileUri;
290313
}
291-
} else {
292-
newSettings[schemaUrl] = fileUri;
293314
}
294-
yamlConfiguration.update('schemas', newSettings);
315+
await yamlConfiguration.update('schemas', newSettings);
295316
}
296317

297-
function writeDisableSchemaDetectionMapping(fileUri: string): void {
318+
async function writeDisableSchemaDetectionMapping(fileUri: string): Promise<void> {
298319
const yamlConfiguration = workspace.getConfiguration('yaml');
299320
const disableSchemaDetection = yamlConfiguration.get('disableSchemaDetection');
300-
yamlConfiguration.update('disableSchemaDetection', addFilePatternToSetting(disableSchemaDetection, fileUri));
321+
await yamlConfiguration.update('disableSchemaDetection', addFilePatternToSetting(disableSchemaDetection, fileUri));
301322
}
302323

303-
function handleSchemaVersionSelection(schema: MatchingJSONSchema, fileUri: string): void {
324+
function handleSchemaVersionSelection(schema: MatchingJSONSchema, fileUri: string, selectedSchemaUris: string[]): void {
304325
const versionPick = window.createQuickPick<SchemaVersionItem>();
305326
const versionItems: SchemaVersionItem[] = [];
306327
const usedVersion = findUsedVersion(schema.versions, schema.uri);
@@ -313,13 +334,18 @@ function handleSchemaVersionSelection(schema: MatchingJSONSchema, fileUri: strin
313334
}
314335

315336
versionPick.items = versionItems;
316-
versionPick.title = `Select JSON Schema version for ${schema.name}`;
337+
versionPick.title = `Select JSON Schema version for ${schema.name ?? schema.uri}`;
317338
versionPick.placeholder = 'Version';
318339
versionPick.onDidHide(() => versionPick.dispose());
319340

320-
versionPick.onDidChangeSelection((items) => {
341+
versionPick.onDidChangeSelection(async (items) => {
321342
if (items && items.length === 1) {
322-
writeSchemaUriMapping(items[0].url, fileUri);
343+
const schemaVersionUris = new Set(Object.values(schema.versions ?? {}));
344+
const remainingSchemaUris = selectedSchemaUris.filter(
345+
(schemaUri) => schemaUri !== schema.uri && !schemaVersionUris.has(schemaUri)
346+
);
347+
const updatedSchemaUris = Array.from(new Set([...remainingSchemaUris, items[0].url]));
348+
await writeSchemaUriMappings(updatedSchemaUris, fileUri);
323349
}
324350
versionPick.hide();
325351
});

0 commit comments

Comments
 (0)