jiebi/CodeConvo huggingface dataset:
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.8.0.tgz#4bc6bb4dbfea5b0d2d7978d6c5cef922d584fb4f" integrity sha512-dlpYPsv0C9S6v6+T/h/d/otSbdUTizMJdxvSoS34tUpMOHev6iW7Zqt5KRFqYxl4vCqpDk9Wmhb3fKL3kwX5fQ== <del> xterm@4.9.0-beta.8: version "4.9.0-beta.8" resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.9.0-beta.8.tgz#ca121934d63f88668d2d5b11d9b2fc3bde7bd805" integrity sha512-EEonYBLANDUBfEeEnHG632bZdgBaAUWst8LFr6oC6f2uLFfJGHQvVJuLaEkPtRvS+jOeoorEXZRPmso1/ANHXA== </del> <ins> xterm@4.9.0-beta.24: version "4.9.0-beta.24" resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.9.0-beta.24.tgz#48f0990c6d32ad3a9bcc4fb49531d7effdaccea5" integrity sha512-CIVxxxbBiSw1WOMkIjjivaiIC9jNgih4klfWsRGx0qiDF5nbzHuLXkPTt+yAQUu9FCyTUwECm9Pkl3gUsSaGKg== </ins> y18n@^3.2.1: version "3.2.1"
{ "view": "scm", "contents": "%view.workbench.scm.empty%", <del> "when": "config.git.enabled && git.state == initialized && workbenchState == empty", </del> <ins> "when": "config.git.enabled && workbenchState == empty", "enablement": "git.state == initialized", </ins> "group": "2_open@1" }, { "view": "scm", "contents": "%view.workbench.scm.folder%", <del> "when": "config.git.enabled && git.state == initialized && workbenchState == folder", </del> <ins> "when": "config.git.enabled && workbenchState == folder", "enablement": "git.state == initialized", </ins> "group": "5_scm@1" }, { "view": "scm", "contents": "%view.workbench.scm.workspace%", "when": "config.git.enabled && git.state == initialized && workbenchState == workspace && workspaceFolderCount != 0", <ins> "enablement": "git.state == initialized", </ins> "group": "5_scm@1" }, { "view": "scm", "contents": "%view.workbench.scm.emptyWorkspace%", <del> "when": "config.git.enabled && git.state == initialized && workbenchState == workspace && workspaceFolderCount == 0", </del> <ins> "when": "config.git.enabled && workbenchState == workspace && workspaceFolderCount == 0", "enablement": "git.state == initialized", </ins> "group": "2_open@1" }, { "view": "explorer", "contents": "%view.workbench.cloneRepository%", <del> "when": "config.git.enabled && git.state == initialized", </del> <ins> "when": "config.git.enabled", "enablement": "git.state == initialized", </ins> "group": "5_scm@1" } ]
"view.workbench.scm.folder": "The folder currently open doesn't have a git repository. You can initialize a repository which will enable source control features powered by git.n[Initialize Repository](command:git.init?%5Btrue%5D)nTo learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).", "view.workbench.scm.workspace": "The workspace currently open doesn't have any folders containing git repositories. You can initialize a repository on a folder which will enable source control features powered by git.n[Initialize Repository](command:git.init)nTo learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).", "view.workbench.scm.emptyWorkspace": "The workspace currently open doesn't have any folders containing git repositories.n[Add Folder to Workspace](command:workbench.action.addRootFolder)nTo learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).", <del> "view.workbench.cloneRepository": "You can also clone a repository from a URL. To learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).n[Clone Repository](command:git.clone)" </del> <ins> "view.workbench.cloneRepository": "You can also clone a repository from a URL. To learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).n[Clone Repository](command:git.clone 'Clone a repository once the git extension has activated')" </ins> }
for (const welcome of contribution.value) { const id = ViewIdentifierMap[welcome.view] ?? welcome.view; const { group, order } = parseGroupAndOrder(welcome, contribution); <ins> const enablement = ContextKeyExpr.deserialize(welcome.enablement); </ins> const disposable = viewsRegistry.registerViewWelcomeContent(id, { content: welcome.contents, when: ContextKeyExpr.deserialize(welcome.when), <ins> // supply the enablement clause for each line in case the line defines a button preconditions: welcome.contents.split('n').map((_) => enablement), </ins> group, order });
type: 'string', description: nls.localize('contributes.viewsWelcome.view.group', "Group to which this welcome content belongs."), }, <ins> [ViewsWelcomeExtensionPointFields.enablement]: { type: 'string', description: nls.localize('contributes.viewsWelcome.view.enablement', "Condition when the welcome content buttons should be enabled."), }, </ins> } } });
Package: @@NAME@@ Version: @@VERSION@@ Section: devel <del> Depends: libnss3 (>= 2:3.26), gnupg, apt, libxkbfile1, libsecret-1-0, libgtk-3-0 (>= 3.10.0), libxss1 </del> <ins> Depends: libnss3 (>= 2:3.26), gnupg, apt, libxkbfile1, libsecret-1-0, libgtk-3-0 (>= 3.10.0), libxss1, libgbm1 </ins> Priority: optional Architecture: @@ARCHITECTURE@@ Maintainer: Microsoft Corporation <vscode-linux@microsoft.com>
"libc.so.6(GLIBC_2.9)(64bit)", "libxcb.so.1()(64bit)", "libxkbfile.so.1()(64bit)", <del> "libsecret-1.so.0()(64bit)" </del> <ins> "libsecret-1.so.0()(64bit)", "libgbm.so.1()(64bit)" </ins> ], "aarch64": [ "libpthread.so.0()(aarch64)",
const onSuccess = async (value: string): Promise<void> => { try { <del> const created = isFolder ? await fileService.createFolder(resources.joinPath(folder.resource, value)) : await textFileService.create(resources.joinPath(folder.resource, value)); </del> <ins> const created = isFolder ? await workingCopyFileService.createFolder(resources.joinPath(folder.resource, value)) : await textFileService.create(resources.joinPath(folder.resource, value)); </ins> await refreshIfSeparator(value, explorerService); isFolder ?
create(resource: URI, contents?: VSBuffer | VSBufferReadable | VSBufferReadableStream, options?: { overwrite?: boolean }): Promise<IFileStatWithMetadata>; /** <ins> * Will create a folder and any parent folder that needs to be created. * * Working copy owners can listen to the `onWillRunWorkingCopyFileOperation` and * `onDidRunWorkingCopyFileOperation` events to participate. * * Note: events will only be emitted for the provided resource, but not any * parent folders that are being created as part of the operation. */ createFolder(resource: URI): Promise<IFileStatWithMetadata>; /** </ins> * Will move working copies matching the provided resources and corresponding children * to the target resources using the associated file service for those resources. *
//#region File operations <del> async create(resource: URI, contents?: VSBuffer | VSBufferReadable | VSBufferReadableStream, options?: { overwrite?: boolean }): Promise<IFileStatWithMetadata> { </del> <ins> create(resource: URI, contents?: VSBuffer | VSBufferReadable | VSBufferReadableStream, options?: { overwrite?: boolean }): Promise<IFileStatWithMetadata> { return this.doCreateFileOrFolder(resource, true, contents, options); } createFolder(resource: URI, options?: { overwrite?: boolean }): Promise<IFileStatWithMetadata> { return this.doCreateFileOrFolder(resource, false); } async doCreateFileOrFolder(resource: URI, isFile: boolean, contents?: VSBuffer | VSBufferReadable | VSBufferReadableStream, options?: { overwrite?: boolean }): Promise<IFileStatWithMetadata> { </ins> // validate create operation before starting <del> const validateCreate = await this.fileService.canCreateFile(resource, options); if (validateCreate instanceof Error) { throw validateCreate; </del> <ins> if (isFile) { const validateCreate = await this.fileService.canCreateFile(resource, options); if (validateCreate instanceof Error) { throw validateCreate; } </ins> } // file operation participant
// now actually create on disk let stat: IFileStatWithMetadata; try { <del> stat = await this.fileService.createFile(resource, contents, { overwrite: options?.overwrite }); </del> <ins> if (isFile) { stat = await this.fileService.createFile(resource, contents, { overwrite: options?.overwrite }); } else { stat = await this.fileService.createFolder(resource); } </ins> } catch (error) { // error event
model1.dispose(); }); <ins> test('createFolder', async function () { let eventCounter = 0; let correlationId: number | undefined = undefined; const resource = toResource.call(this, '/path/folder'); const participant = accessor.workingCopyFileService.addFileOperationParticipant({ participate: async (files, operation) => { assert.equal(files.length, 1); const file = files[0]; assert.equal(file.target.toString(), resource.toString()); assert.equal(operation, FileOperation.CREATE); eventCounter++; } }); const listener1 = accessor.workingCopyFileService.onWillRunWorkingCopyFileOperation(e => { assert.equal(e.files.length, 1); const file = e.files[0]; assert.equal(file.target.toString(), resource.toString()); assert.equal(e.operation, FileOperation.CREATE); correlationId = e.correlationId; eventCounter++; }); const listener2 = accessor.workingCopyFileService.onDidRunWorkingCopyFileOperation(e => { assert.equal(e.files.length, 1); const file = e.files[0]; assert.equal(file.target.toString(), resource.toString()); assert.equal(e.operation, FileOperation.CREATE); assert.equal(e.correlationId, correlationId); eventCounter++; }); await accessor.workingCopyFileService.createFolder(resource); assert.equal(eventCounter, 3); participant.dispose(); listener1.dispose(); listener2.dispose(); }); </ins> async function testEventsMoveOrCopy(files: { source: URI, target: URI }[], move?: boolean): Promise<number> { let eventCounter = 0;
move(_source: URI, _target: URI, _overwrite?: boolean): Promise<IFileStatWithMetadata> { return Promise.resolve(null!); } copy(_source: URI, _target: URI, _overwrite?: boolean): Promise<IFileStatWithMetadata> { return Promise.resolve(null!); } createFile(_resource: URI, _content?: VSBuffer | VSBufferReadable, _options?: ICreateFileOptions): Promise<IFileStatWithMetadata> { return Promise.resolve(null!); } <del> createFolder(_resource: URI): Promise<IFileStatWithMetadata> { throw new Error('not implemented'); } </del> <ins> createFolder(_resource: URI): Promise<IFileStatWithMetadata> { return Promise.resolve(null!); } </ins> onDidChangeFileSystemProviderRegistrations = Event.None;
getDirty(resource: URI): IWorkingCopy[] { return []; } create(resource: URI, contents?: VSBuffer | VSBufferReadable | VSBufferReadableStream, options?: { overwrite?: boolean | undefined; } | undefined): Promise<IFileStatWithMetadata> { throw new Error('Method not implemented.'); } <ins> createFolder(resource: URI): Promise<IFileStatWithMetadata> { throw new Error('Method not implemented.'); } </ins> move(files: { source: URI; target: URI; }[], options?: { overwrite?: boolean }): Promise<IFileStatWithMetadata[]> { throw new Error('Method not implemented.'); }
pattern: string; <del> constructor(base: vscode.WorkspaceFolder | string, pattern: string) { </del> <ins> constructor(base: URI | vscode.WorkspaceFolder | string, pattern: string) { </ins> if (typeof base !== 'string') { <del> if (!base || !URI.isUri(base.uri)) { </del> <ins> if (!base || !URI.isUri(base) && !URI.isUri(base.uri)) { </ins> throw illegalArgument('base'); } }
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { getCurrentAuthenticationSessionInfo } from 'vs/workbench/services/authentication/browser/authenticationService'; <del> import { AuthenticationSession, IAuthenticationService } from 'vs/workbench/services/authentication/common/authentication'; </del> <ins> import { AuthenticationSessionAccount, IAuthenticationService } from 'vs/workbench/services/authentication/common/authentication'; </ins> import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IProductService } from 'vs/platform/product/common/productService';
import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; <ins> import { ILogService } from 'vs/platform/log/common/log'; </ins> export class ViewContainerActivityAction extends ActivityAction {
static readonly ACCOUNTS_VISIBILITY_PREFERENCE_KEY = 'workbench.activity.showAccounts'; <ins> private readonly groupedAccounts: Map<string, (AuthenticationSessionAccount & { canSignOut: boolean })[]> = new Map(); private readonly problematicProviders: Set<string> = new Set(); private initialized = false; private sessionFromEmbedder = getCurrentAuthenticationSessionInfo(this.credentialsService, this.productService); </ins> constructor( action: ActivityAction, contextMenuActionsProvider: () => IAction[],
return actions; } <ins> //#endregion //#region groupedAccounts helpers private async addOrUpdateAccount(providerId: string, account: AuthenticationSessionAccount): Promise<void> { const accounts = this.groupedAccounts.get(providerId); const existingAccount = accounts?.find(a => a.id === account.id); if (existingAccount) { if (existingAccount.label !== account.label) { existingAccount.label = account.label; } return; } const sessionFromEmbedder = await this.sessionFromEmbedder; // If the session stored from the embedder allows sign out, then we can treat it and all others as sign out-able let canSignOut = !!sessionFromEmbedder?.canSignOut; if (!canSignOut) { if (sessionFromEmbedder?.id) { const sessions = (await this.authenticationService.getSessions(providerId)).filter(s => s.account.id === account.id); canSignOut = !sessions.some(s => s.id === sessionFromEmbedder.id); } else { // The default if we don't have a session from the embedder is to allow sign out canSignOut = true; } } if (!accounts) { this.groupedAccounts.set(providerId, [{ ...account, canSignOut }]); return; } accounts.push({ ...account, canSignOut }); } private removeAccount(providerId: string, account: AuthenticationSessionAccount): void { const accounts = this.groupedAccounts.get(providerId); if (!accounts) { return; } const index = accounts.findIndex(a => a.id === account.id); if (index === -1) { return; } accounts.splice(index, 1); if (accounts.length === 0) { this.groupedAccounts.delete(providerId); } } private async addAccountsFromProvider(providerId: string): Promise<void> { try { const sessions = await this.authenticationService.getSessions(providerId); this.problematicProviders.delete(providerId); const result = await Promise.allSettled(sessions.map(s => this.addOrUpdateAccount(providerId, s.account))); for (const r of result) { if (r.status === 'rejected') { this.logService.error(r.reason); } } } catch (e) { this.logService.error(e); this.problematicProviders.add(providerId); } } //#endregion </ins> } export interface IProfileActivity extends IActivity {
import { Event } from 'vs/base/common/event'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; <ins> export interface AuthenticationSessionAccount { label: string; id: string; } </ins> export interface AuthenticationSession { id: string; accessToken: string; <del> account: { label: string; id: string; }; </del> <ins> account: AuthenticationSessionAccount; </ins> scopes: ReadonlyArray<string>; idToken?: string; }
import { exec } from 'child_process'; import * as resources from 'vs/base/common/resources'; import * as fs from 'fs'; <ins> import * as pfs from 'vs/base/node/pfs'; </ins> import { isLinux } from 'vs/base/common/platform'; import { IExtHostTunnelService, TunnelDto } from 'vs/workbench/api/common/extHostTunnelService'; import { asPromise } from 'vs/base/common/async'; import { Event, Emitter } from 'vs/base/common/event'; import { TunnelOptions, TunnelCreationOptions } from 'vs/platform/remote/common/tunnel'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; <ins> import { promisify } from 'util'; import { MovingAverage } from 'vs/base/common/numbers'; </ins> class ExtensionTunnel implements vscode.Tunnel { private _onDispose: Emitter<void> = new Emitter();
) { super(); this._proxy = extHostRpc.getProxy(MainContext.MainThreadTunnelService); <del> if (initData.remote.isRemote && initData.remote.authority && !process.env['VSCODE_DISABLE_PROC_READING']) { </del> <ins> if (initData.remote.isRemote && initData.remote.authority) { </ins> this.registerCandidateFinder(); } }
return this._proxy.$getTunnels(); } <del> registerCandidateFinder(): void { // Every two seconds, scan to see if the candidate ports have changed; if (isLinux) { let oldPorts: { host: string, port: number, detail: string }[] | undefined = undefined; setInterval(async () => { const newPorts = await this.findCandidatePorts(); if (!oldPorts || (JSON.stringify(oldPorts) !== JSON.stringify(newPorts))) { oldPorts = newPorts; this._proxy.$onFoundNewCandidates(oldPorts.filter(async (candidate) => await this._showCandidatePort(candidate.host, candidate.port, candidate.detail))); return; } }, 2000); </del> <ins> private calculateDelay(movingAverage: number) { // Some local testing indicated that the moving average might be between 50-100 ms. return Math.max(movingAverage * 20, 2000); } async registerCandidateFinder(): Promise<void> { // Regularly scan to see if the candidate ports have changed. if (!isLinux) { return; } let movingAverage = new MovingAverage(); let oldPorts: { host: string, port: number, detail: string }[] | undefined = undefined; while (1) { const startTime = new Date().getTime(); const newPorts = await this.findCandidatePorts(); const timeTaken = new Date().getTime() - startTime; movingAverage.update(timeTaken); if (!oldPorts || (JSON.stringify(oldPorts) !== JSON.stringify(newPorts))) { oldPorts = newPorts; await this._proxy.$onFoundNewCandidates(oldPorts.filter(async (candidate) => await this._showCandidatePort(candidate.host, candidate.port, candidate.detail))); } await (new Promise<void>(resolve => setTimeout(() => resolve(), this.calculateDelay(movingAverage.value)))); </ins> } }
let tcp: string = ''; let tcp6: string = ''; try { <del> tcp = fs.readFileSync('/proc/net/tcp', 'utf8'); tcp6 = fs.readFileSync('/proc/net/tcp6', 'utf8'); </del> <ins> tcp = await pfs.readFile('/proc/net/tcp', 'utf8'); tcp6 = await pfs.readFile('/proc/net/tcp6', 'utf8'); </ins> } catch (e) { // File reading error. No additional handling needed. }
}); })); <del> const procChildren = fs.readdirSync('/proc'); const processes: { pid: number, cwd: string, cmd: string }[] = []; </del> <ins> const procChildren = await pfs.readdir('/proc'); const processes: { pid: number, cwd: string, cmd: string }[] = []; </ins> for (let childName of procChildren) { try { const pid: number = Number(childName); const childUri = resources.joinPath(URI.file('/proc'), childName); <del> const childStat = fs.statSync(childUri.fsPath); </del> <ins> const childStat = await pfs.stat(childUri.fsPath); </ins> if (childStat.isDirectory() && !isNaN(pid)) { <del> const cwd = fs.readlinkSync(resources.joinPath(childUri, 'cwd').fsPath); const cmd = fs.readFileSync(resources.joinPath(childUri, 'cmdline').fsPath, 'utf8'); </del> <ins> const cwd = await promisify(fs.readlink)(resources.joinPath(childUri, 'cwd').fsPath); const cmd = await pfs.readFile(resources.joinPath(childUri, 'cmdline').fsPath, 'utf8'); </ins> processes.push({ pid, cwd, cmd }); } } catch (e) {
// Menu registration - open editors <ins> const isFileOrUntitledResourceContextKey = ContextKeyExpr.or(ResourceContextKey.IsFileSystemResource, ResourceContextKey.Scheme.isEqualTo(Schemas.untitled)); </ins> const openToSideCommand = { id: OPEN_TO_SIDE_COMMAND_ID, title: nls.localize('openToSide', "Open to the Side")
group: '3_compare', order: 20, command: compareResourceCommand, <del> when: ContextKeyExpr.and(ResourceContextKey.HasResource, ResourceSelectedForCompareContext, WorkbenchListDoubleSelection.toNegated()) </del> <ins> when: ContextKeyExpr.and(ResourceContextKey.HasResource, ResourceSelectedForCompareContext, isFileOrUntitledResourceContextKey, WorkbenchListDoubleSelection.toNegated()) </ins> }); const selectForCompareCommand = {
group: '3_compare', order: 30, command: selectForCompareCommand, <del> when: ContextKeyExpr.and(ResourceContextKey.HasResource, WorkbenchListDoubleSelection.toNegated()) </del> <ins> when: ContextKeyExpr.and(ResourceContextKey.HasResource, isFileOrUntitledResourceContextKey, WorkbenchListDoubleSelection.toNegated()) </ins> }); const compareSelectedCommand = {
group: '3_compare', order: 30, command: compareSelectedCommand, <del> when: ContextKeyExpr.and(ResourceContextKey.HasResource, WorkbenchListDoubleSelection) </del> <ins> when: ContextKeyExpr.and(ResourceContextKey.HasResource, WorkbenchListDoubleSelection, isFileOrUntitledResourceContextKey) </ins> }); MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, {
if (this.accessor.partOptions.wrapTabs) { const visibleTabsWidth = tabsContainer.offsetWidth; const allTabsWidth = tabsContainer.scrollWidth; <ins> const lastTabFitsWrapped = () => { const lastTab = this.getLastTab(); if (!lastTab) { return true; // no tab always fits } return lastTab.offsetWidth <= (dimensions.available.width - editorToolbarContainer.offsetWidth); }; </ins> // If tabs wrap or should start to wrap (when width exceeds visible width) // we must trigger `updateWrapping` to set the `last-tab-margin-right` // accordingly based on the number of actions. The margin is important to // properly position the last tab apart from the actions <del> if (tabsWrapMultiLine || allTabsWidth > visibleTabsWidth) { </del> <ins> // // We already check here if the last tab would fit when wrapped given the // editor toolbar will also show right next to it. This ensures we are not // enabling wrapping only to disable it again in the code below (this fixes // flickering issue https://github.com/microsoft/vscode/issues/115050) if (tabsWrapMultiLine || (allTabsWidth > visibleTabsWidth && lastTabFitsWrapped())) { </ins> updateTabsWrapping(true); } // Tabs wrap multiline: remove wrapping under certain size constraint conditions if (tabsWrapMultiLine) { <del> const lastTab = this.getLastTab(); </del> if ( (tabsContainer.offsetHeight > dimensions.available.height) ||											// if height exceeds available height (allTabsWidth === visibleTabsWidth && tabsContainer.offsetHeight === TabsTitleControl.TAB_HEIGHT) ||	// if wrapping is not needed anymore <del> (lastTab && lastTab.offsetWidth > (dimensions.available.width - editorToolbarContainer.offsetWidth))	// if editor actions occupy too much space </del> <ins> (!lastTabFitsWrapped())																					// if last tab does not fit anymore </ins> ) { updateTabsWrapping(false); }
if (lens.command) { const title = renderLabelWithIcons(lens.command.title.trim()); if (lens.command.id) { <del> children.push(dom.$('a', { id: String(i) }, ...title)); </del> <ins> children.push(dom.$('a', { id: String(i), title: lens.command.tooltip }, ...title)); </ins> this._commands.set(String(i), lens.command); } else { <del> children.push(dom.$('span', undefined, ...title)); </del> <ins> children.push(dom.$('span', { title: lens.command.tooltip }, ...title)); </ins> } if (i + 1 < lenses.length) { children.push(dom.$('span', undefined, 'u00a0|u00a0'));
* Submitting pull requests ## Related Projects <del> Many of the core components and extensions to Code live in their own repositories on GitHub. For example, the [node debug adapter](https://github.com/microsoft/vscode-node-debug), the [mono debug adapter](https://github.com/microsoft/vscode-mono-debug). </del> <ins> Many of the core components and extensions to Code live in their own repositories on GitHub. For example, the [node debug adapter](https://github.com/microsoft/vscode-node-debug) and the [mono debug adapter](https://github.com/microsoft/vscode-mono-debug). </ins> For a complete list, please see the [Related Projects](https://github.com/Microsoft/vscode/wiki/Related-Projects) page on our wiki.