Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
105 commits
Select commit Hold shift + click to select a range
a04454c
Add config parsing
andrewbranch Jul 9, 2026
7b5485c
Add span mapping, wire into file loading and module resolution with m…
andrewbranch Jul 9, 2026
40dc747
Wire into build mode / tsbuildinfo
andrewbranch Jul 10, 2026
b825fb8
Use node_modules package.json as manifest/identity
andrewbranch Jul 10, 2026
9ccd3aa
IPC infrastructure
andrewbranch Jul 13, 2026
054b2db
Wire up to tsc
andrewbranch Jul 13, 2026
06b5a2d
Exclude content-mapped files from emit
andrewbranch Jul 13, 2026
7b0f76e
Reorganize tests
andrewbranch Jul 14, 2026
bb14819
Wire up to editor
andrewbranch Jul 15, 2026
f44a957
Wire up diagnostics in-editor
andrewbranch Jul 16, 2026
5e9e866
Aggregate synthesized diagnostics
andrewbranch Jul 16, 2026
352be3a
Refactor spanmap to allow gaps as synthesized ranges
andrewbranch Jul 16, 2026
90d043c
Plumb position mapping through language service
andrewbranch Jul 20, 2026
a4cdbdf
Fourslash tests
andrewbranch Jul 20, 2026
5a9dcca
Implement extension API for first-wake
andrewbranch Jul 21, 2026
4195584
Enable declaration emit
andrewbranch Jul 21, 2026
a2b5d99
Support auto-importing from content-mapped files, fix project watch f…
andrewbranch Jul 21, 2026
ebd66ac
Allow content-mapped files in inferred projects
andrewbranch Jul 21, 2026
5037cf5
Fix fourslash tests
andrewbranch Jul 21, 2026
78f1cbc
Add module path completion test
andrewbranch Jul 21, 2026
e9ea490
Handle unregistering content mappers on config change
andrewbranch Jul 22, 2026
44d398c
Support in --watch and --build --watch
andrewbranch Jul 22, 2026
1536047
Include span maps and original text in IPC API
andrewbranch Jul 22, 2026
aca0729
Some renaming
andrewbranch Jul 22, 2026
40e4d51
Merge remote-tracking branch 'upstream/main' into content-mappers
andrewbranch Jul 22, 2026
2dadf89
Pass locale to content mappers in initialize
andrewbranch Jul 22, 2026
6312de0
Add SpanMappingPurpose, support one original to multiple generated ra…
andrewbranch Jul 22, 2026
b88a2a2
Optimize generated to original lookups
andrewbranch Jul 22, 2026
3b05081
Self-review corrections
andrewbranch Jul 22, 2026
78d1771
Add SpanMapKindAlias
andrewbranch Jul 23, 2026
a98a33d
More self-review fixes, move diagnostic source to initialize result
andrewbranch Jul 23, 2026
b29347c
Copilot review feedback
andrewbranch Jul 23, 2026
ff4912e
Generate
andrewbranch Jul 23, 2026
d2c3fb7
Fix leaked child processes in --build
andrewbranch Jul 23, 2026
1244045
Improve content mapper failure diagnostics
andrewbranch Jul 23, 2026
8343320
More copilot review
andrewbranch Jul 23, 2026
f075aa3
Generate
andrewbranch Jul 23, 2026
1d38c36
Fix auto-import default binding name in non-TS-extensioned files
andrewbranch Jul 23, 2026
0484716
Register semantic tokens capability for content-mapped files
mikearnaldi Jul 23, 2026
dea0c50
Preserve content mappers in the typings-augmented command line
mikearnaldi Jul 24, 2026
d2153a5
Include content mapper extensions in wildcard directory globs
mikearnaldi Jul 24, 2026
31dc32b
Format, add more tests
andrewbranch Jul 24, 2026
9138cf2
SpanMapPurpose -> SpanMapFeature
andrewbranch Aug 3, 2026
2504282
Merge branch 'main' into content-mappers
andrewbranch Aug 3, 2026
3584eb7
Handle locale changes mid-session
andrewbranch Aug 4, 2026
e0a79f2
Support mapper options, including config read from external files
andrewbranch Aug 4, 2026
6469d0b
Only dynamic-config mappers receive openProject
andrewbranch Aug 4, 2026
b48ba86
Support supplemental outpus
andrewbranch Aug 7, 2026
20271fe
Fix position mapping at span ends
andrewbranch Aug 7, 2026
bddd216
Add canonical/supplemental file names to content-mapped files in API
andrewbranch Aug 7, 2026
86603a6
Support extension-bundled content mappers
andrewbranch Aug 10, 2026
c4e8955
Merge branch 'main' into content-mappers
andrewbranch Aug 10, 2026
4279873
Complete rename
andrewbranch Aug 10, 2026
c18f834
Fix failing test from merge with main
andrewbranch Aug 11, 2026
c0c909b
Merge branch 'main' into content-mappers
andrewbranch Aug 11, 2026
be27354
Support emit
andrewbranch Aug 11, 2026
d8cf221
Revert emit support, keeping static extension maps
andrewbranch Aug 11, 2026
9bf0d84
Merge branch 'main' into content-mappers
andrewbranch Aug 11, 2026
9a44db3
Fix baselines
andrewbranch Aug 11, 2026
ca4ee99
Improve initialization diagnostics
andrewbranch Aug 12, 2026
e18bbad
Add timings in --extendedDiagnostics and LSP logs
andrewbranch Aug 12, 2026
761d221
Fix content mapper manifest watching in LSP
andrewbranch Aug 12, 2026
5fada77
Add VS Code config setting for disabling content mappers
andrewbranch Aug 12, 2026
d2dd4c0
Vocabulary change
andrewbranch Aug 12, 2026
e38d659
Test cleanup
andrewbranch Aug 12, 2026
7ce1c49
Suppress unused variable diagnostics in synthesized code
andrewbranch Aug 12, 2026
f8e9a73
Add support for custom ignore/expect diagnostic directives
andrewbranch Aug 13, 2026
08475bb
Go back to supporting per-transform virtual extension
andrewbranch Aug 13, 2026
cabf4d2
Self-review batch 1
andrewbranch Aug 13, 2026
8d599a6
Fix extension synchronization issues with config changes and restarts
andrewbranch Aug 13, 2026
9cac0b0
Self-review batch 3
andrewbranch Aug 13, 2026
9cb40d5
Fix virtual filename affecting module status and ensure transform ide…
andrewbranch Aug 13, 2026
8b7be5d
Fix external diagnostics in build/incremental
andrewbranch Aug 13, 2026
7daff9a
Fix watch issues
andrewbranch Aug 13, 2026
9a2a327
Fix LS features that should use intersecting ranges
andrewbranch Aug 13, 2026
cb5a186
Fix remaining LS features, enable other ones
andrewbranch Aug 14, 2026
c3e65db
Merge branch 'main' into content-mappers
andrewbranch Aug 14, 2026
f61847a
Merge branch 'main' into content-mappers
andrewbranch Aug 14, 2026
a4b7bb7
Remove flaky test assertion
andrewbranch Aug 14, 2026
6a7e16e
Add TS_CONTENT_MAPPER_DEBUG environment variable
andrewbranch Aug 14, 2026
06dd419
Rename --loadExternalPlugins to --runExternalCode
andrewbranch Aug 14, 2026
e72cbea
Rename tsContentMapper to typescript.contentMapper
andrewbranch Aug 14, 2026
961da98
Make diagnosticDirectives format more compact
andrewbranch Aug 14, 2026
e21f4c0
Add missing content mapper fields to TS API
andrewbranch Aug 14, 2026
d07c1ff
Skip invalid ranges when deciding where to insert auto-imports
andrewbranch Aug 14, 2026
6b3824e
Merge branch 'main' into content-mappers
andrewbranch Aug 17, 2026
a041075
Inherit contentMappers
andrewbranch Aug 18, 2026
e95a024
Always send open/close project
andrewbranch Aug 18, 2026
7860b9e
Support option diagnostics
andrewbranch Aug 18, 2026
1a0fcb2
Add build/incremental test
andrewbranch Aug 18, 2026
3e9efcc
Merge branch 'main' into content-mappers
andrewbranch Aug 18, 2026
20183c9
Remove extra params from Transform request
andrewbranch Aug 18, 2026
11926f2
PR feedback
andrewbranch Aug 19, 2026
2955d1e
Fail fast after mapper connection errors
andrewbranch Aug 19, 2026
c30d7ea
Preserve mapped parse cache keys
andrewbranch Aug 19, 2026
a515dfb
Fix runExternalCode API flag
andrewbranch Aug 19, 2026
0f1a8d6
Fix terminating launcher processes
andrewbranch Aug 19, 2026
1bc575c
Fix false positive diagnostic deduplication and code actions confusin…
andrewbranch Aug 19, 2026
8c48ec2
Prevent nil dereference during race with cancelation
andrewbranch Aug 19, 2026
f0d15b9
Fix refreshing content mappers in LSP after more complex watch changes
andrewbranch Aug 19, 2026
d29ee6d
Fix watch fast path when one mapped file and one normal file are chan…
andrewbranch Aug 19, 2026
a3fdd56
Fix build mode exit status when content mapper init fails
andrewbranch Aug 19, 2026
bdc0478
Fix output mapping of hover and signature help previews
andrewbranch Aug 19, 2026
96ef22d
Return null on rename when edits conflict or overlap
andrewbranch Aug 19, 2026
a831231
More PR review
andrewbranch Aug 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
19 changes: 12 additions & 7 deletions Herebyfile.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,12 @@ const enumDefs = [
{ name: "ModuleDetectionKind", goPrefix: "ModuleDetectionKind", goFile: "internal/core/compileroptions.go", outDir: "_packages/native-preview/src/enums" },
{ name: "NewLineKind", goPrefix: "NewLineKind", goFile: "internal/core/compileroptions.go", outDir: "_packages/native-preview/src/enums" },
{ name: "JsxEmit", goPrefix: "JsxEmit", goFile: "internal/core/compileroptions.go", outDir: "_packages/native-preview/src/enums" },
{ name: "ScriptKind", goPrefix: "ScriptKind", goFile: "internal/core/scriptkind.go", outDir: "_packages/native-preview/src/enums" },
{ name: "TokenFlags", goPrefix: "TokenFlags", goFile: "internal/ast/tokenflags.go", outDir: "_packages/native-preview/src/enums" },
{ name: "DiagnosticDirectivePolicy", goPrefix: "MappedDiagnosticDirectivePolicy", goFile: "internal/ast/ast.go", outDir: "_packages/native-preview/src/enums" },
{ name: "SpanMapKind", goPrefix: "Kind", goFile: "internal/spanmap/spanmap.go", outDir: "_packages/native-preview/src/enums" },
{ name: "SpanMapFidelity", goPrefix: "Fidelity", goFile: "internal/spanmap/spanmap.go", outDir: "_packages/native-preview/src/enums" },
{ name: "SpanMapFeature", goPrefix: "Feature", goFile: "internal/spanmap/spanmap.go", outDir: "_packages/native-preview/src/enums" },
{ name: "NodeBuilderFlags", goPrefix: "Flags", goFile: "internal/nodebuilder/types.go", outDir: "_packages/native-preview/src/enums" },
{ name: "CompletionItemKind", goPrefix: "CompletionItemKind", goFile: "internal/lsp/lsproto/lsp_generated.go", outDir: "_packages/native-preview/src/enums" },
{ name: "EmitOnly", goPrefix: "Emit", goFile: "internal/compiler/emitter.go", outDir: "_packages/native-preview/src/enums", excludeMembers: ["OnlyBuilderSignature"] },
Expand All @@ -370,7 +375,7 @@ function parseGoConstBlock(block, def) {
const prefix = def.goPrefix;
const members = [];
let iotaCounter = 0;
let hasIota = false;
let iotaExpression;

for (const rawLine of block.split("\n")) {
const line = rawLine.replace(/\/\/.*$/, "").trim();
Expand All @@ -379,7 +384,7 @@ function parseGoConstBlock(block, def) {
// Match: PrefixName Type = value or PrefixName = value
const fullMatch = line.match(new RegExp(`^(${prefix}\\w+)\\s+(?:\\S+\\s*)?=\\s*(.+)$`));
// Match bare iota continuation: just PrefixName
const bareMatch = !fullMatch && hasIota
const bareMatch = !fullMatch && iotaExpression !== undefined
? line.match(new RegExp(`^(${prefix}\\w+)$`))
: null;

Expand All @@ -393,12 +398,12 @@ function parseGoConstBlock(block, def) {
if (def.stringEnum) {
tsValue = parseGoStringValue(goValue, def.valueReplacements ?? {});
}
else if (goValue === "iota") {
tsValue = String(iotaCounter);
hasIota = true;
else if (goValue.includes("iota")) {
iotaExpression = goValue;
tsValue = goValue.replace(/\biota\b/g, String(iotaCounter));
}
else if (hasIota && goValue === "") {
tsValue = String(iotaCounter);
else if (iotaExpression !== undefined && goValue === "") {
tsValue = iotaExpression.replace(/\biota\b/g, String(iotaCounter));
}
else {
// Replace Go bitwise NOT (^) with TypeScript (~)
Expand Down
9 changes: 9 additions & 0 deletions _extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,15 @@
"experimental"
]
},
"js/ts.contentMappers.enabled": {
"type": "boolean",
"default": true,
"description": "%native-preview.contentMappers.enabled.description%",
"scope": "window",
"tags": [
"experimental"
]
},
"js/ts.server.goMemLimit": {
"type": "string",
"description": "%native-preview.goMemLimit.description%",
Expand Down
1 change: 1 addition & 0 deletions _extension/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"native-preview.tsserver.tsdk.description": "Path to the @typescript/native-preview package or tsgo binary directory. If not specified, the extension will look for it in the default location.",
"native-preview.additionalTsdkLocations.description": "Additional paths to tsgo binary directories or @typescript/native-preview packages that should appear in the version selector.",
"native-preview.showDebugInfo.description": "Show debug information (PID, executable path) in the server menu.",
"native-preview.contentMappers.enabled.description": "Enable external content mappers that transform otherwise unsupported file types for TypeScript language features. Content mappers can be configured in tsconfig.json/jsconfig.json files or provided by VS Code extensions.",
"native-preview.goMemLimit.description": "Set GOMEMLIMIT for the language server (e.g., '2048MiB', '4GiB'). See https://pkg.go.dev/runtime#hdr-Environment_Variables for more information.",
"native-preview.goMemLimit.error": "Must be a valid memory limit (e.g., '2048MiB', '4GiB').",
"native-preview.customConfigFileName.deprecation": "This setting has moved to js/ts.customConfigFileName.",
Expand Down
148 changes: 140 additions & 8 deletions _extension/src/client.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as vscode from "vscode";

import { CancellationToken } from "vscode-languageclient";
import {
ClientCapabilities,
CloseAction,
Expand All @@ -23,12 +24,14 @@ import {
configurationMiddleware,
sendNotificationMiddleware,
} from "./configurationMiddleware";
import type { SerializedContentMapperContribution } from "./contentMapperContributions";
import { registerMultiDocumentHighlightFeature } from "./languageFeatures/documentHighlight";
import { registerHoverFeature } from "./languageFeatures/hover";
import { registerOnAutoInsertFeature } from "./languageFeatures/onAutoInsert";
import { registerSourceDefinitionFeature } from "./languageFeatures/sourceDefinition";
import * as tr from "./telemetryReporting";
import {
contentMappersEnabled,
ExeInfo,
getExe,
jsTsLanguageModes,
Expand All @@ -38,6 +41,24 @@ import {
import { getLanguageForUri } from "./util";
import { workspaceSymbolSendRequestMiddleware } from "./workspaceSymbolMiddleware";

// Registration IDs the server uses for content mapper capabilities all share this prefix (see
// RegisterContentMapperExtensions in internal/lsp/server.go). The extension watches for these dynamic
// registrations to learn which file extensions are content-mapped.
const contentMapperRegistrationPrefix = "content-mapper-";

// ContentMapperRegisterOptions is the subset of a dynamic registration's options that our server sends
// for content mapper capabilities: a document selector of simple glob-pattern filters (see
// RegisterContentMapperExtensions in internal/lsp/server.go).
interface ContentMapperRegisterOptions {
documentSelector?: ReadonlyArray<{ pattern: string; }>;
}

// extractPatternFilters reuses the server's glob-pattern filters so the extension's custom providers match
// the same content-mapped files.
function extractPatternFilters(registerOptions: ContentMapperRegisterOptions | undefined): vscode.DocumentFilter[] {
return (registerOptions?.documentSelector ?? []).map(({ pattern }) => ({ pattern }));
}

export class Client implements vscode.Disposable {
private outputChannel: vscode.LogOutputChannel;
private initializedEventEmitter: vscode.EventEmitter<void>;
Expand All @@ -52,6 +73,14 @@ export class Client implements vscode.Disposable {
private disposables: vscode.Disposable[] = [];
isInitialized = false;

// Document filters for content-mapped file extensions, keyed by the server's registration ID. These
// augment the static jsTs document selector so the extension's custom language-feature providers
// (hover, multi-document highlight, on-auto-insert) also cover content-mapped files.
private contentMapperFiltersById = new Map<string, vscode.DocumentFilter[]>();
// Disposables for the custom providers registered against the current (selector-scoped) document set.
// Re-registered whenever the set of content-mapped extensions changes.
private selectorScopedFeatures: vscode.Disposable[] = [];

private exe: ExeInfo | undefined;
private errorHandler: ReportingErrorHandler;

Expand Down Expand Up @@ -85,6 +114,7 @@ export class Client implements vscode.Disposable {
codeLensShowLocationsCommandName,
enableTelemetry: true,
logVerbosity: this.outputChannel.logLevel,
runExternalCode: contentMappersEnabled(),
},
errorHandler: this.errorHandler,
middleware: {
Expand All @@ -100,6 +130,18 @@ export class Client implements vscode.Disposable {
sendNotification: sendNotificationMiddleware,
sendRequest: workspaceSymbolSendRequestMiddleware,
provideHover: () => undefined,
handleRegisterCapability: async (params, next) => {
await next(params, CancellationToken.None);
if (this.trackContentMapperRegistrations(params.registrations)) {
this.registerSelectorScopedFeatures();
}
},
handleUnregisterCapability: async (params, next) => {
await next(params, CancellationToken.None);
if (this.trackContentMapperUnregistrations(params.unregisterations)) {
this.registerSelectorScopedFeatures();
}
},
},
diagnosticCollectionName: "typescript-push",
diagnosticPullOptions: {
Expand Down Expand Up @@ -135,10 +177,16 @@ export class Client implements vscode.Disposable {
}

if (selector.pattern !== undefined) {
// VS Code's glob matcher is not available via the API;
// see: https://github.com/microsoft/vscode/issues/237304
// But, we're only called on selectors passed above, so just ignore this for now.
throw new Error("Not implemented");
// VS Code's full glob matcher is not available via the API
// (microsoft/vscode#237304), but content mapper registrations only ever
// use simple "**/*<ext>" patterns, so match those by suffix. Any other
// pattern falls through to the next selector.
const glob = typeof selector.pattern === "string" ? selector.pattern : selector.pattern.pattern;
const globPrefix = "**/*";
if (glob.startsWith(globPrefix) && resource.path.endsWith(glob.slice(globPrefix.length))) {
return true;
}
continue;
}

return true;
Expand Down Expand Up @@ -197,9 +245,9 @@ export class Client implements vscode.Disposable {
},
};

// Refresh the initial log verbosity in case the output channel's log
// level changed between construction and start.
// Refresh options in case they changed between construction and start.
this.clientOptions.initializationOptions.logVerbosity = this.outputChannel.logLevel;
this.clientOptions.initializationOptions.runExternalCode = contentMappersEnabled();
this.clientOptions.initializationOptions.trackFlakyDiagnostics = effectiveflakesFlag !== "never" ? (effectiveflakesFlag === "panic" ? 2 : 1) : 0;

this.client = new NativePreviewLanguageClient(
Expand Down Expand Up @@ -265,11 +313,77 @@ export class Client implements vscode.Disposable {
this.disposables.push(
logLevelListener,
serverTelemetryListener,
registerMultiDocumentHighlightFeature(this.documentSelector, this.client),
registerSourceDefinitionFeature(this.client),
registerHoverFeature(this.documentSelector, this.client),
registerOnAutoInsertFeature(this.documentSelector, this.client),
);
// Register the selector-scoped custom providers (hover, multi-document highlight). These start
// scoped to the static jsTs selector and expand as content-mapped extensions register.
this.registerSelectorScopedFeatures();
}

// trackContentMapperRegistrations records the content-mapped document filters carried by the server's
// dynamic capability registrations. Returns whether the known set of content-mapper filters changed.
private trackContentMapperRegistrations(registrations: ReadonlyArray<{ id: string; registerOptions?: ContentMapperRegisterOptions; }>): boolean {
let changed = false;
for (const registration of registrations) {
if (!registration.id.startsWith(contentMapperRegistrationPrefix)) {
continue;
}
const filters = extractPatternFilters(registration.registerOptions);
if (filters.length > 0) {
this.contentMapperFiltersById.set(registration.id, filters);
changed = true;
}
else if (this.contentMapperFiltersById.delete(registration.id)) {
changed = true;
}
}
return changed;
}

// trackContentMapperUnregistrations forgets the filters for any content-mapper registration the server
// has removed. Returns whether the known set of content-mapper filters changed.
private trackContentMapperUnregistrations(unregistrations: ReadonlyArray<{ id: string; }>): boolean {
let changed = false;
for (const unregistration of unregistrations) {
if (this.contentMapperFiltersById.delete(unregistration.id)) {
changed = true;
}
}
return changed;
}

// selectorScopedDocumentSelector is the static jsTs selector plus every active content-mapper filter,
// deduplicated. The custom providers are registered against it so they also cover content-mapped files.
private selectorScopedDocumentSelector(): vscode.DocumentSelector {
const seen = new Set<string>();
const filters: vscode.DocumentFilter[] = [];
for (const list of this.contentMapperFiltersById.values()) {
for (const filter of list) {
const key = typeof filter.pattern === "string" ? filter.pattern : JSON.stringify(filter.pattern);
if (!seen.has(key)) {
seen.add(key);
filters.push(filter);
}
}
}
return [...this.documentSelector, ...filters];
}

// registerSelectorScopedFeatures (re)registers the custom language-feature providers whose scope must
// track the set of content-mapped extensions. Existing registrations are disposed first.
private registerSelectorScopedFeatures(): void {
for (const disposable of this.selectorScopedFeatures.splice(0)) {
Comment thread
andrewbranch marked this conversation as resolved.
disposable.dispose();
}
if (!this.client || this.isStopping || this.isDisposed) {
return;
}
const selector = this.selectorScopedDocumentSelector();
this.selectorScopedFeatures.push(
registerMultiDocumentHighlightFeature(selector, this.client),
registerHoverFeature(selector, this.client),
);
}

async stop(): Promise<void> {
Expand All @@ -278,6 +392,10 @@ export class Client implements vscode.Disposable {
}
this.isStopping = true;
this.isInitialized = false;
for (const disposable of this.selectorScopedFeatures.splice(0)) {
disposable.dispose();
}
this.contentMapperFiltersById.clear();
const disposables = this.disposables.splice(0);
await Promise.all(disposables.map(d => d.dispose()));
await this.client?.stop();
Expand All @@ -290,6 +408,10 @@ export class Client implements vscode.Disposable {
this.isDisposed = true;
this.isStopping = true;
this.isInitialized = false;
for (const disposable of this.selectorScopedFeatures.splice(0)) {
disposable.dispose();
}
this.contentMapperFiltersById.clear();
const disposables = this.disposables.splice(0);
await Promise.all(disposables.map(d => d.dispose()));
await this.client?.dispose();
Expand Down Expand Up @@ -399,6 +521,16 @@ export class Client implements vscode.Disposable {
textDocument: { uri },
}, token);
}

async setContentMapperContributions(contributions: readonly SerializedContentMapperContribution[], openDocuments: readonly vscode.Uri[]): Promise<void> {
if (!this.client || !this.isInitialized) {
return;
}
await this.client.sendRequest("custom/setContentMapperContributions", {
contributions: contentMappersEnabled() ? contributions : [],
openDocuments: openDocuments.map(uri => ({ uri: uri.toString() })),
});
}
}

// Returns true when running on a VS Code Insiders build.
Expand Down
Loading
Loading