Skip to content

Commit 1bf5644

Browse files
shin19991207datho7561
authored andcommitted
implement auto-diasbling schema detection for Docker DX, GitHub Actions, Azure Piplines if corresponding extension is enabled
Signed-off-by: Morgan Chang <shin19991207@gmail.com>
1 parent ed60d53 commit 1bf5644

4 files changed

Lines changed: 154 additions & 3 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ The following settings are supported:
5050
* `yaml.hoverSchemaSource`: Enable/disable showing the schema source in hover tooltips
5151
* `yaml.completion`: Enable/disable autocompletion
5252
* `yaml.schemas`: Helps you associate schemas with files in a glob pattern
53+
* `yaml.disableSchemaDetection`: Disable schema detection for YAML files matching the configured glob pattern or list of glob patterns. Common patterns are automatically added when another enabled extension provides more specific YAML support, such as [Docker DX](https://marketplace.visualstudio.com/items?itemName=docker.docker), [GitHub Actions](https://marketplace.visualstudio.com/items?itemName=github.vscode-github-actions), or [Azure Pipelines](https://marketplace.visualstudio.com/items?itemName=ms-azure-devops.azure-pipelines). Modelines still apply.
5354
* `yaml.schemaStore.enable`: When set to true, the YAML language server will pull in all available schemas from [JSON Schema Store](http://schemastore.org/)
5455
* `yaml.schemaStore.url`: URL of a schema store catalog to use when downloading schemas.
5556
* `yaml.customTags`: Array of custom tags that the parser will validate against. It has three ways to be used. A tag without a type, such as "!Ref", is treated as a scalar tag. A tag with a node type, such as "!Ref sequence", specifies the YAML node type that the tag is written on. A tag with a node type and return type, such as "!FindInMap sequence:string", also specifies the schema type that the tagged value evaluates to. Supported node types are scalar, sequence, and mapping. Supported return types are string, number, integer, boolean, null, array, and object. The return type aliases scalar, sequence, and mapping are accepted as string, array, and object.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@
153153
}
154154
],
155155
"default": [],
156-
"markdownDescription": "Disable schema detection for YAML files matching the configured glob pattern or glob patterns. The expected value is a glob pattern or list of glob patterns. Modelines still apply."
156+
"markdownDescription": "Disable schema detection for YAML files matching the configured glob pattern or list of glob patterns. Common patterns are automatically added when another enabled extension provides more specific YAML support, such as [Docker DX](https://marketplace.visualstudio.com/items?itemName=docker.docker), [GitHub Actions](https://marketplace.visualstudio.com/items?itemName=github.vscode-github-actions), or [Azure Pipelines](https://marketplace.visualstudio.com/items?itemName=ms-azure-devops.azure-pipelines). Modelines still apply."
157157
},
158158
"yaml.kubernetesCRDStore.enable": {
159159
"type": "boolean",

src/autoDisableSchemaDetection.ts

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) IBM Corp. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
import { ConfigurationTarget, extensions, workspace } from 'vscode';
6+
import type { ExtensionContext, WorkspaceConfiguration } from 'vscode';
7+
8+
interface AutoDisableSchemaDetectionEntry {
9+
extensionId: string;
10+
configurationKey?: string;
11+
fileMatches: string[];
12+
}
13+
14+
const autoDisableSchemaDetectionEntries: AutoDisableSchemaDetectionEntry[] = [
15+
{
16+
extensionId: 'docker.docker',
17+
configurationKey: 'docker.extension.enableComposeLanguageServer',
18+
fileMatches: [
19+
'**/docker-compose.yml',
20+
'**/docker-compose.yaml',
21+
'**/docker-compose.*.yml',
22+
'**/docker-compose.*.yaml',
23+
'**/compose.yml',
24+
'**/compose.yaml',
25+
'**/compose.*.yml',
26+
'**/compose.*.yaml',
27+
],
28+
},
29+
{
30+
extensionId: 'github.vscode-github-actions',
31+
fileMatches: [
32+
'**/.github/workflows/*.yml',
33+
'**/.github/workflows/*.yaml',
34+
'**/.gitea/workflows/*.yml',
35+
'**/.gitea/workflows/*.yaml',
36+
'**/.forgejo/workflows/*.yml',
37+
'**/.forgejo/workflows/*.yaml',
38+
],
39+
},
40+
{
41+
extensionId: 'ms-azure-devops.azure-pipelines',
42+
fileMatches: ['azure-pipelines.yml', 'azure-pipelines.yaml'],
43+
},
44+
];
45+
46+
export async function initializeAutoDisableSchemaDetection(context: ExtensionContext): Promise<void> {
47+
await updateAutoDisableSchemaDetectionSetting();
48+
context.subscriptions.push(
49+
extensions.onDidChange(() => {
50+
updateAutoDisableSchemaDetectionSetting();
51+
}),
52+
workspace.onDidChangeConfiguration((event) => {
53+
if (
54+
event.affectsConfiguration('docker.extension.enableComposeLanguageServer') ||
55+
event.affectsConfiguration('yaml.disableSchemaDetection')
56+
) {
57+
updateAutoDisableSchemaDetectionSetting();
58+
}
59+
})
60+
);
61+
}
62+
63+
async function updateAutoDisableSchemaDetectionSetting(): Promise<void> {
64+
const configuration = workspace.getConfiguration('yaml');
65+
const currentDisableSchemaDetection = getDisableSchemaDetectionSetting(configuration);
66+
const update = computeAutoDisableSchemaDetectionUpdate(
67+
currentDisableSchemaDetection.value,
68+
getAutoDisabledSchemaDetectionExtensions()
69+
);
70+
if (
71+
!(
72+
currentDisableSchemaDetection.value.length === update.length &&
73+
currentDisableSchemaDetection.value.every((value, index) => value === update[index])
74+
)
75+
) {
76+
await configuration.update('disableSchemaDetection', update, currentDisableSchemaDetection.target);
77+
}
78+
}
79+
80+
function getDisableSchemaDetectionSetting(
81+
configuration: WorkspaceConfiguration
82+
): {
83+
value: string[];
84+
target: ConfigurationTarget;
85+
} {
86+
const inspected = configuration.inspect<string | string[]>('disableSchemaDetection');
87+
if (inspected?.workspaceValue !== undefined) {
88+
return {
89+
value: toStringArray(inspected.workspaceValue),
90+
target: ConfigurationTarget.Workspace,
91+
};
92+
}
93+
return {
94+
value: toStringArray(inspected?.globalValue ?? []),
95+
target: ConfigurationTarget.Global,
96+
};
97+
}
98+
99+
export function getAutoDisabledSchemaDetectionExtensions(): string[] {
100+
const configuration = workspace.getConfiguration();
101+
const enabledExtensions: string[] = [];
102+
for (const entry of autoDisableSchemaDetectionEntries) {
103+
if (
104+
!extensions.getExtension(entry.extensionId) ||
105+
(entry.configurationKey && configuration.get<boolean>(entry.configurationKey, true) === false)
106+
) {
107+
continue;
108+
}
109+
enabledExtensions.push(entry.extensionId);
110+
}
111+
return enabledExtensions;
112+
}
113+
114+
export function computeAutoDisableSchemaDetectionUpdate(
115+
currentDisableSchemaDetection: string[],
116+
enabledExtensions: string[]
117+
): string[] {
118+
const desiredFileMatches = getFileMatchesForExtensions(enabledExtensions);
119+
const disabledExtensions = autoDisableSchemaDetectionEntries
120+
.map((entry) => entry.extensionId)
121+
.filter((extensionId) => !enabledExtensions.includes(extensionId));
122+
const disabledFileMatches = getFileMatchesForExtensions(disabledExtensions);
123+
const withoutInactiveManagedFileMatches = currentDisableSchemaDetection.filter(
124+
(fileMatch) => !disabledFileMatches.includes(fileMatch) || desiredFileMatches.includes(fileMatch)
125+
);
126+
const additions = desiredFileMatches.filter((fileMatch) => !withoutInactiveManagedFileMatches.includes(fileMatch));
127+
return withoutInactiveManagedFileMatches.concat(additions);
128+
}
129+
130+
function getFileMatchesForExtensions(extensionIds: string[]): string[] {
131+
const fileMatches: string[] = [];
132+
for (const entry of autoDisableSchemaDetectionEntries) {
133+
if (extensionIds.includes(entry.extensionId)) {
134+
fileMatches.push(...entry.fileMatches);
135+
}
136+
}
137+
return fileMatches;
138+
}
139+
140+
function toStringArray(value: unknown): string[] {
141+
if (typeof value === 'string') {
142+
return [value];
143+
}
144+
if (Array.isArray(value)) {
145+
return value.filter((item): item is string => typeof item === 'string');
146+
}
147+
return [];
148+
}

src/extension.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { getConflictingExtensions, showUninstallConflictsNotification } from './
2121
import { TelemetryErrorHandler, TelemetryOutputChannel } from './telemetry';
2222
import { createJSONSchemaStatusBarItem } from './schema-status-bar-item';
2323
import { initializeRecommendation } from './recommendation';
24+
import { initializeAutoDisableSchemaDetection } from './autoDisableSchemaDetection';
2425

2526
export interface ISchemaAssociations {
2627
[pattern: string]: string[];
@@ -107,11 +108,11 @@ export interface TelemetryService {
107108
sendStartupEvent(): Promise<void>;
108109
}
109110

110-
export function startClient(
111+
export async function startClient(
111112
context: ExtensionContext,
112113
newLanguageClient: LanguageClientConstructor,
113114
runtime: RuntimeEnvironment
114-
): SchemaExtensionAPI {
115+
): Promise<SchemaExtensionAPI> {
115116
const telemetryErrorHandler = new TelemetryErrorHandler(runtime.telemetry, lsName, 4);
116117
const outputChannel = window.createOutputChannel(lsName);
117118
const l10nPath = context.asAbsolutePath('./dist/l10n');
@@ -144,6 +145,7 @@ export function startClient(
144145

145146
// Create the language client and start it
146147
client = newLanguageClient('yaml', lsName, clientOptions);
148+
await initializeAutoDisableSchemaDetection(context);
147149

148150
const disposable = client.start();
149151

0 commit comments

Comments
 (0)